<?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-01-16T14:00:00+00:00</updated>
  <id>https://realpython.com/</id>
  <author>
    <name>Real Python</name>
  </author>

  
    <entry>
      <title>Async IO in Python: A Complete Walkthrough</title>
      <id>https://realpython.com/async-io-python/</id>
      <link href="https://realpython.com/async-io-python/"/>
      <updated>2019-01-16T14:00:00+00:00</updated>
      <summary>This tutorial will give you a firm grasp of Python’s approach to async IO, which is a concurrent programming design that has received dedicated support in Python, evolving rapidly from Python 3.4 through 3.7 (and probably beyond).</summary>
      <content type="html">
        &lt;p&gt;Async IO is a concurrent programming design that has received dedicated support in Python, evolving rapidly from Python 3.4 through 3.7, and &lt;a href=&quot;https://twitter.com/1st1/status/1041855365745455104&quot;&gt;probably beyond&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You may be thinking with dread, &amp;ldquo;Concurrency, parallelism, threading, multiprocessing.  That&amp;rsquo;s a lot to grasp already.  Where does async IO fit in?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This tutorial is built to help you answer that question, giving you a firmer grasp of Python&amp;rsquo;s approach to async IO.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here&amp;rsquo;s what you&amp;rsquo;ll cover:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Asynchronous IO (async IO)&lt;/strong&gt;: a language-agnostic paradigm (model) that has implementations across a host of programming languages&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;&lt;/strong&gt;: two new Python keywords that are used to define coroutines&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;asyncio&lt;/code&gt;&lt;/strong&gt;: the Python package that provides a foundation and API for running and managing coroutines&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Coroutines (specialized generator functions) are the heart of async IO in Python, and we&amp;rsquo;ll dive into them later on.&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;: In this article, I use the term &lt;strong&gt;async IO&lt;/strong&gt; to denote the language-agnostic design of asynchronous IO, while &lt;code&gt;asyncio&lt;/code&gt; refers to the Python package.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Before you get started, you&amp;rsquo;ll need to make sure you&amp;rsquo;re set up to use &lt;code&gt;asyncio&lt;/code&gt; and other libraries found in this tutorial.&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;setting-up-your-environment&quot;&gt;Setting Up Your Environment&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ll need Python 3.7 or above to follow this article in its entirety, as well as the &lt;code&gt;aiohttp&lt;/code&gt; and &lt;code&gt;aiofiles&lt;/code&gt; 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; python3.7 -m venv ./py37async
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; ./py37async/bin/activate  &lt;span class=&quot;c1&quot;&gt;# Windows: .\py37async\Scripts\activate.bat&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install --upgrade pip aiohttp aiofiles  &lt;span class=&quot;c1&quot;&gt;# Optional: aiodns&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For help with installing Python 3.7 and setting up a virtual environment, check out &lt;a href=&quot;https://realpython.com/installing-python/&quot;&gt;Python 3 Installation &amp;amp; Setup Guide&lt;/a&gt; or &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;Virtual Environments Primer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With that, let&amp;rsquo;s jump in.&lt;/p&gt;
&lt;h2 id=&quot;the-10000-foot-view-of-async-io&quot;&gt;The 10,000-Foot View of Async IO&lt;/h2&gt;
&lt;p&gt;Async IO is a bit lesser known than its tried-and-true cousins, multiprocessing and threading.  This section will give you a fuller picture of what async IO is and how it fits into its surrounding landscape.&lt;/p&gt;
&lt;h3 id=&quot;where-does-async-io-fit-in&quot;&gt;Where Does Async IO Fit In?&lt;/h3&gt;
&lt;p&gt;Concurrency and parallelism are expansive subjects that are not easy to wade into.  While this article focuses on async IO and its implementation in Python, it&amp;rsquo;s worth taking a minute to compare async IO to its counterparts in order to have context about how async IO fits into the larger, sometimes dizzying puzzle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parallelism&lt;/strong&gt; consists of performing multiple operations at the same time.  &lt;strong&gt;Multiprocessing&lt;/strong&gt; is a means to effect parallelism, and it entails spreading tasks over a computer&amp;rsquo;s central processing units (CPUs, or cores).  Multiprocessing is well-suited for CPU-bound tasks: tightly bound &lt;code&gt;for&lt;/code&gt; loops and mathematical computations usually fall into this category.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concurrency&lt;/strong&gt; is a slightly broader term than parallelism.  It suggests that multiple tasks have the ability to run in an overlapping manner.  (There&amp;rsquo;s a saying that concurrency does not imply parallelism.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Threading&lt;/strong&gt; is a concurrent execution model whereby multiple &lt;a href=&quot;https://en.wikipedia.org/wiki/Thread_(computing)&quot;&gt;threads&lt;/a&gt; take turns executing tasks.  One process can contain multiple threads.  Python has a complicated relationship with threading thanks to its &lt;a href=&quot;https://realpython.com/python-gil/&quot;&gt;GIL&lt;/a&gt;, but that&amp;rsquo;s beyond the scope of this article.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s important to know about threading is that it&amp;rsquo;s better for IO-bound tasks.  While a CPU-bound task is characterized by the computer&amp;rsquo;s cores continually working hard from start to finish, an IO-bound job is dominated by a lot of waiting on input/output to complete.&lt;/p&gt;
&lt;p&gt;To recap the above, concurrency encompasses both multiprocessing (ideal for CPU-bound tasks) and threading (suited for IO-bound tasks).  Multiprocessing is a form of parallelism, with parallelism being a specific type (subset) of concurrency.  The Python standard library has offered longstanding &lt;a href=&quot;https://docs.python.org/3/library/concurrency.html&quot;&gt;support for both of these&lt;/a&gt; through its &lt;code&gt;multiprocessing&lt;/code&gt;, &lt;code&gt;threading&lt;/code&gt;, and &lt;code&gt;concurrent.futures&lt;/code&gt; packages.&lt;/p&gt;
&lt;p&gt;Now it&amp;rsquo;s time to bring a new member to the mix.  Over the last few years, a separate design has been more comprehensively built into CPython: asynchronous IO, enabled through the standard library&amp;rsquo;s &lt;code&gt;asyncio&lt;/code&gt; package and the new &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; language keywords.  To be clear, async IO is not a newly invented concept, and it has existed or is being built into other languages and runtime environments, such as &lt;a href=&quot;https://gobyexample.com/goroutines&quot;&gt;Go&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/async&quot;&gt;C#&lt;/a&gt;, or &lt;a href=&quot;https://docs.scala-lang.org/sips/async.html&quot;&gt;Scala&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;asyncio&lt;/code&gt; package is billed by the Python documentation as &lt;a href=&quot;https://docs.python.org/3/library/asyncio.html&quot;&gt;a library to write concurrent code&lt;/a&gt;.  However, async IO is not threading, nor is it multiprocessing.  It is not built on top of either of these.&lt;/p&gt;
&lt;p&gt;In fact, async IO is a single-threaded, single-process design: it uses &lt;strong&gt;cooperative multitasking&lt;/strong&gt;, a term that you&amp;rsquo;ll flesh out by the end of this tutorial.  It has been said in other words that async IO gives a feeling of concurrency despite using a single thread in a single process.  Coroutines (a central feature of async IO) can be scheduled concurrently, but they are not inherently concurrent.&lt;/p&gt;
&lt;p&gt;To reiterate, async IO is a style of concurrent programming, but it is not parallelism.  It&amp;rsquo;s more closely aligned with threading than with multiprocessing but is very much distinct from both of these and is a standalone member in concurrency&amp;rsquo;s bag of tricks.&lt;/p&gt;
&lt;p&gt;That leaves one more term.  What does it mean for something to be &lt;strong&gt;asynchronous&lt;/strong&gt;?  This isn&amp;rsquo;t a rigorous definition, but for our purposes here, I can think of two properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Asynchronous routines are able to &amp;ldquo;pause&amp;rdquo; while waiting on their ultimate result and let other routines run in the meantime.&lt;/li&gt;
&lt;li&gt;Asynchronous code, through the mechanism above, facilitates concurrent execution.  To put it differently, asynchronous code gives the look and feel of concurrency.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s a diagram to put it all together.  The white terms represent concepts, and the green terms represent ways in which they are implemented or effected:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screen_Shot_2018-10-17_at_3.18.44_PM.c02792872031.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-70&quot; src=&quot;https://files.realpython.com/media/Screen_Shot_2018-10-17_at_3.18.44_PM.c02792872031.jpg&quot; width=&quot;504&quot; height=&quot;411&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screen_Shot_2018-10-17_at_3.18.44_PM.c02792872031.jpg&amp;amp;w=126&amp;amp;sig=49a0ee12fcb52037d02ba7dba0959616a45e72f1 126w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screen_Shot_2018-10-17_at_3.18.44_PM.c02792872031.jpg&amp;amp;w=252&amp;amp;sig=de481cd508527d65e641c75e8d98cde592b82d0f 252w, https://files.realpython.com/media/Screen_Shot_2018-10-17_at_3.18.44_PM.c02792872031.jpg 504w&quot; sizes=&quot;75vw&quot; alt=&quot;Concurrency versus parallelism&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll stop there on the comparisons between concurrent programming models.  This tutorial is focused on the subcomponent that is async IO, how to use it, and the APIs that have sprung up around it.  For a thorough exploration of threading versus multiprocessing versus async IO, pause here and check out Jim Anderson&amp;rsquo;s &lt;a href=&quot;https://realpython.com/python-concurrency/&quot;&gt;overview of concurrency in Python&lt;/a&gt;.  Jim is way funnier than me and has sat in more meetings than me, to boot.&lt;/p&gt;
&lt;h3 id=&quot;async-io-explained&quot;&gt;Async IO Explained&lt;/h3&gt;
&lt;p&gt;Async IO may at first seem counterintuitive and paradoxical.  How does something that facilitates concurrent code use a single thread and a single CPU core?  I&amp;rsquo;ve never been very good at conjuring up examples, so I&amp;rsquo;d like to paraphrase one from Miguel Grinberg&amp;rsquo;s 2017 PyCon talk, which explains everything quite beautifully:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chess master Judit Polgár hosts a chess exhibition in which she plays multiple amateur players.  She has two ways of conducting the exhibition: synchronously and asynchronously.&lt;/p&gt;
&lt;p&gt;Assumptions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;24 opponents&lt;/li&gt;
&lt;li&gt;Judit makes each chess move in 5 seconds&lt;/li&gt;
&lt;li&gt;Opponents each take 55 seconds to make a move&lt;/li&gt;
&lt;li&gt;Games average 30 pair-moves (60 moves total)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Synchronous version&lt;/strong&gt;: Judit plays one game at a time, never two at the same time, until the game is complete.  Each game takes &lt;em&gt;(55 + 5) * 30 == 1800&lt;/em&gt; seconds, or 30 minutes.  The entire exhibition takes &lt;em&gt;24 * 30 == 720&lt;/em&gt; minutes, or &lt;strong&gt;12 hours&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Asynchronous version&lt;/strong&gt;: Judit moves from table to table, making one move at each table.  She leaves the table and lets the opponent make their next move during the wait time.  One move on all 24 games takes Judit &lt;em&gt;24 * 5 == 120&lt;/em&gt; seconds, or 2 minutes.  The entire exhibition is now cut down to &lt;em&gt;120 * 30 == 3600&lt;/em&gt; seconds, or just &lt;strong&gt;1 hour&lt;/strong&gt;. &lt;a href=&quot;https://youtu.be/iG6fr81xHKA?t=4m29s&quot;&gt;(Source)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is only one Judit Polgár, who has only two hands and makes only one move at a time by herself.  But playing asynchronously cuts the exhibition time down from 12 hours to one.  So, cooperative multitasking is a fancy way of saying that a program&amp;rsquo;s event loop (more on that later) communicates with multiple tasks to let each take turns running at the optimal time.&lt;/p&gt;
&lt;p&gt;Async IO takes long waiting periods in which functions would otherwise be blocking and allows other functions to run during that downtime.  (A function that blocks effectively forbids others from running from the time that it starts until the time that it returns.)&lt;/p&gt;
&lt;h3 id=&quot;async-io-is-not-easy&quot;&gt;Async IO Is Not Easy&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve heard it said, &amp;ldquo;Use async IO when you can; use threading when you must.&amp;rdquo;  The truth is that building durable multithreaded code can be hard and error-prone.  Async IO avoids some of the potential speedbumps that you might otherwise encounter with a threaded design.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s not to say that async IO in Python is easy.  Be warned: when you venture a bit below the surface level, async programming can be difficult too!  Python&amp;rsquo;s async model is built around concepts such as callbacks, events, transports, protocols, and futures&amp;mdash;just the terminology can be intimidating.  The fact that its API has been changing continually makes it no easier.&lt;/p&gt;
&lt;p&gt;Luckily, &lt;code&gt;asyncio&lt;/code&gt; has matured to a point where most of its features are no longer provisional, while its documentation has received a huge overhaul and some quality resources on the subject are starting to emerge as well.&lt;/p&gt;
&lt;h2 id=&quot;the-asyncio-package-and-asyncawait&quot;&gt;The &lt;code&gt;asyncio&lt;/code&gt; Package and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Now that you have some background on async IO as a design, let&amp;rsquo;s explore Python&amp;rsquo;s implementation.  Python&amp;rsquo;s &lt;code&gt;asyncio&lt;/code&gt; package (introduced in Python 3.4) and its two keywords, &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;, serve different purposes but come together to help you declare, build, execute, and manage asynchronous code.&lt;/p&gt;
&lt;h3 id=&quot;the-asyncawait-syntax-and-native-coroutines&quot;&gt;The &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; Syntax and Native Coroutines&lt;/h3&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;A Word of Caution&lt;/strong&gt;: Be careful what you read out there on the Internet.  Python&amp;rsquo;s async IO API has evolved rapidly from Python 3.4 to Python 3.7.  Some old patterns are no longer used, and some things that were at first disallowed are now allowed through new introductions.  For all I know, this tutorial will join the club of the outdated soon too.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;At the heart of async IO are coroutines.  A coroutine is a specialized version of a Python generator function.  Let&amp;rsquo;s start with a baseline definition and then build off of it as you progress here: a coroutine is a function that can suspend its execution before reaching &lt;code&gt;return&lt;/code&gt;, and it can indirectly pass control to another coroutine for some time.&lt;/p&gt;
&lt;p&gt;Later, you&amp;rsquo;ll dive a lot deeper into how exactly the traditional generator is repurposed into a coroutine.  For now, the easiest way to pick up how coroutines work is to start making some.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take the immersive approach and write some async IO code.  This short program is the &lt;code&gt;Hello World&lt;/code&gt; of async IO but goes a long way towards illustrating its core functionality:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# countasync.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;count&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;One&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;1&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;Two&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&lt;/span&gt;&lt;span class=&quot;p&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;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&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;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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&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;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&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;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;o&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;perf_counter&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;s&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;{__file__}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; executed in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{elapsed:0.2f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&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 you execute this file, take note of what looks different than if you were to define the functions with just &lt;code&gt;def&lt;/code&gt; and &lt;code&gt;time.sleep()&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; python3 countasync.py
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;countasync.py executed in 1.01 seconds.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The order of this output is the heart of async IO.  Talking to each of the calls to &lt;code&gt;count()&lt;/code&gt; is a single event loop, or coordinator.  When each task reaches &lt;code&gt;await asyncio.sleep(1)&lt;/code&gt;, the function yells up to the event loop and gives control back to it, saying, &amp;ldquo;I&amp;rsquo;m going to be sleeping for 1 second.  Go ahead and let something else meaningful be done in the meantime.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Contrast this to the synchronous version:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# countsync.py&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;count&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;One&amp;quot;&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;1&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;Two&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;main&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;_&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;count&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;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&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;perf_counter&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;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;o&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;perf_counter&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;s&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;{__file__}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; executed in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{elapsed:0.2f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&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 executed, there is a slight but critical change in order and execution time:&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 countsync.py
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;countsync.py executed in 3.01 seconds.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While using &lt;code&gt;time.sleep()&lt;/code&gt; and &lt;code&gt;asyncio.sleep()&lt;/code&gt; may seem banal,  they are used as stand-ins for any time-intensive processes that involve wait time.  (The most mundane thing you can wait on is a &lt;code&gt;sleep()&lt;/code&gt; call that does basically nothing.)  That is, &lt;code&gt;time.sleep()&lt;/code&gt; can represent any time-consuming blocking function call, while &lt;code&gt;asyncio.sleep()&lt;/code&gt; is used to stand in for a non-blocking call (but one that also takes some time to complete).&lt;/p&gt;
&lt;p&gt;As you&amp;rsquo;ll see in the next section, the benefit of awaiting something, including &lt;code&gt;asyncio.sleep()&lt;/code&gt;, is that the surrounding function can temporarily cede control to another function that&amp;rsquo;s more readily able to do something immediately.  In contrast, &lt;code&gt;time.sleep()&lt;/code&gt; or any other blocking call is incompatible with asynchronous Python code, because it will stop everything in its tracks for the duration of the sleep time.&lt;/p&gt;
&lt;h3 id=&quot;the-rules-of-async-io&quot;&gt;The Rules of Async IO&lt;/h3&gt;
&lt;p&gt;At this point, a more formal definition of &lt;code&gt;async&lt;/code&gt;, &lt;code&gt;await&lt;/code&gt;, and the coroutine functions that they create are in order.  This section is a little dense, but getting a hold of &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; is instrumental, so come back to this if you need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The syntax &lt;code&gt;async def&lt;/code&gt; introduces either a &lt;strong&gt;native coroutine&lt;/strong&gt; or an &lt;strong&gt;asynchronous generator&lt;/strong&gt;.  The expressions &lt;code&gt;async with&lt;/code&gt; and &lt;code&gt;async for&lt;/code&gt; are also valid, and you&amp;rsquo;ll see them later on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The keyword &lt;code&gt;await&lt;/code&gt; passes function control back to the event loop.  (It suspends the execution of the surrounding coroutine.)  If Python encounters an &lt;code&gt;await f()&lt;/code&gt; expression in the scope of &lt;code&gt;g()&lt;/code&gt;, this is how &lt;code&gt;await&lt;/code&gt; tells the event loop, &amp;ldquo;Suspend execution of &lt;code&gt;g()&lt;/code&gt; until whatever I&amp;rsquo;m waiting on&amp;mdash;the result of &lt;code&gt;f()&lt;/code&gt;&amp;mdash;is returned.  In the meantime, go let something else run.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In code, that second bullet point looks roughly 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;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Pause here and come back to g() when f() is ready&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;There&amp;rsquo;s also a strict set of rules around when and how you can and cannot use &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;.  These can be handy whether you are still picking up the syntax or already have exposure to using &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A function that you introduce with &lt;code&gt;async def&lt;/code&gt; is a coroutine.  It may use &lt;code&gt;await&lt;/code&gt;, &lt;code&gt;return&lt;/code&gt;, or &lt;code&gt;yield&lt;/code&gt;, but all of these are optional.  Declaring &lt;code&gt;async def noop(): pass&lt;/code&gt; is valid:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Using &lt;code&gt;await&lt;/code&gt; and/or &lt;code&gt;return&lt;/code&gt; creates a coroutine function.  To call a coroutine function, you must &lt;code&gt;await&lt;/code&gt; it to get its results.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It is less common (and only recently legal in Python) to use &lt;code&gt;yield&lt;/code&gt; in an &lt;code&gt;async def&lt;/code&gt; block. This creates an &lt;a href=&quot;https://www.python.org/dev/peps/pep-0525/&quot;&gt;asynchronous generator&lt;/a&gt;, which you iterate over with &lt;code&gt;async for&lt;/code&gt;.  Forget about async generators for the time being and focus on getting down the syntax for coroutine functions, which use &lt;code&gt;await&lt;/code&gt; and/or &lt;code&gt;return&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Anything defined with &lt;code&gt;async def&lt;/code&gt; may not use &lt;code&gt;yield from&lt;/code&gt;, which will raise a &lt;code&gt;SyntaxError&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Just like it&amp;rsquo;s a &lt;code&gt;SyntaxError&lt;/code&gt; to use &lt;code&gt;yield&lt;/code&gt; outside of a &lt;code&gt;def&lt;/code&gt; function, it is a &lt;code&gt;SyntaxError&lt;/code&gt; to use &lt;code&gt;await&lt;/code&gt; outside of an &lt;code&gt;async def&lt;/code&gt; coroutine.  You can only use &lt;code&gt;await&lt;/code&gt; in the body of coroutines.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are some terse examples meant to summarize the above few rules:&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;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;f&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;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&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;c1&quot;&gt;# OK - `await` and `return` allowed in coroutines&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;g&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;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# OK - this is an async generator&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;m&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;yield from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gen&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;c1&quot;&gt;# No - SyntaxError&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;m&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;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&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;c1&quot;&gt;# Still no - SyntaxError (no `async def` here)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, when you use &lt;code&gt;await f()&lt;/code&gt;, it&amp;rsquo;s required that &lt;code&gt;f()&lt;/code&gt; be an object that is &lt;a href=&quot;https://docs.python.org/3/reference/datamodel.html#awaitable-objects&quot;&gt;awaitable&lt;/a&gt;.  Well, that&amp;rsquo;s not very helpful, is it?  For now, just know that an awaitable object is either (1) another coroutine or (2) an object defining an &lt;code&gt;.__await__()&lt;/code&gt; dunder method that returns an iterator.  If you&amp;rsquo;re writing a program, for the large majority of purposes, you should only need to worry about case #1.&lt;/p&gt;
&lt;p&gt;That brings us to one more technical distinction that you may see pop up: an older way of marking a function as a coroutine is to decorate a normal &lt;code&gt;def&lt;/code&gt; function with &lt;code&gt;@asyncio.coroutine&lt;/code&gt;.  The result is a &lt;strong&gt;generator-based coroutine&lt;/strong&gt;.  This construction has been outdated since the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; syntax was put in place in Python 3.5.&lt;/p&gt;
&lt;p&gt;These two coroutines are essentially equivalent (both are awaitable), but the first is &lt;strong&gt;generator-based&lt;/strong&gt;, while the second is a &lt;strong&gt;native coroutine&lt;/strong&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;asyncio&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coroutine&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;py34_coro&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;Generator-based coroutine, older syntax&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;py35_coro&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;Native coroutine, modern syntax&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you&amp;rsquo;re writing any code yourself, prefer native coroutines for the sake of being explicit rather than implicit.  Generator-based coroutines will be &lt;a href=&quot;https://docs.python.org/3/library/asyncio-task.html#generator-based-coroutines&quot;&gt;removed&lt;/a&gt; in Python 3.10.&lt;/p&gt;
&lt;p&gt;Towards the latter half of this tutorial, we&amp;rsquo;ll touch on generator-based coroutines for explanation&amp;rsquo;s sake only.  The reason that &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; were introduced is to make coroutines a standalone feature of Python that can be easily differentiated from a normal generator function, thus reducing ambiguity.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t get bogged down in generator-based coroutines, which have been &lt;a href=&quot;https://www.python.org/dev/peps/pep-0492/#rationale-and-goals&quot;&gt;deliberately outdated&lt;/a&gt; by &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;.  They have their own small set of rules (for instance, &lt;code&gt;await&lt;/code&gt; cannot be used in a generator-based coroutine) that are largely irrelevant if you stick to the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; syntax.&lt;/p&gt;
&lt;p&gt;Without further ado, let&amp;rsquo;s take on a few more involved examples.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s one example of how async IO cuts down on wait time: given a coroutine &lt;code&gt;makerandom()&lt;/code&gt; that keeps producing random integers in the range [0, 10], until one of them exceeds a threshold, you want to let multiple calls of this coroutine not need to wait for each other to complete in succession.  You can largely follow the patterns from the two scripts above, with slight 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;ch&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# rand.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&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;c1&quot;&gt;# ANSI colors&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;c&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;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[0m&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# End of color&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[36m&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Cyan&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[91m&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Red&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[35m&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Magenta&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;randint&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;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;b&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;o&quot;&gt;-&amp;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;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;randint&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;makerandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&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;threshold&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;o&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;o&quot;&gt;-&amp;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;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&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;o&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;Initiated makerandom(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{idx}&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;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&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;0&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;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threshold&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;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&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;o&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;makerandom(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{idx}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) == &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; too low; retrying.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;n&quot;&gt;idx&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;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&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;0&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&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;o&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;---&amp;gt; Finished: makerandom(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{idx}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) == &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&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;c&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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&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;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&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;n&quot;&gt;makerandom&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;mi&quot;&gt;10&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;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;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;3&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;res&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;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;444&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&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;span class=&quot;nb&quot;&gt;print&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;s2&quot;&gt;&amp;quot;r1: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{r1}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, r2: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{r2}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, r3: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{r3}&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;The colorized output says a lot more than I can and gives you a sense for how this script is carried out:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/asyncio-rand.dffdd83b4256.gif&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-70&quot; src=&quot;https://files.realpython.com/media/asyncio-rand.dffdd83b4256.gif&quot; width=&quot;300&quot; height=&quot;400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/asyncio-rand.dffdd83b4256.gif&amp;amp;w=75&amp;amp;sig=1730fc7b8bca67960626d95ccf73c50dfe4c66dd 75w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/asyncio-rand.dffdd83b4256.gif&amp;amp;w=150&amp;amp;sig=35259ed0d3ddd6b7d3165c31498b67d657715b21 150w, https://files.realpython.com/media/asyncio-rand.dffdd83b4256.gif 300w&quot; sizes=&quot;75vw&quot; alt=&quot;rand.py program execution&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;rand.py execution&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;This program uses one main coroutine, &lt;code&gt;makerandom()&lt;/code&gt;, and runs it concurrently across 3 different inputs.  Most programs will contain small, modular coroutines and one wrapper function that serves to chain each of the smaller coroutines together.  &lt;code&gt;main()&lt;/code&gt; is then used to gather tasks (futures) by mapping the central coroutine across some iterable or pool.&lt;/p&gt;
&lt;p&gt;In this miniature example, the pool is &lt;code&gt;range(3)&lt;/code&gt;.  In a fuller example presented later, it is a set of URLs that need to be requested, parsed, and processed concurrently, and &lt;code&gt;main()&lt;/code&gt; encapsulates that entire routine for each URL.&lt;/p&gt;
&lt;p&gt;While &amp;ldquo;making random integers&amp;rdquo; (which is CPU-bound more than anything) is maybe not the greatest choice as a candidate for &lt;code&gt;asyncio&lt;/code&gt;, it&amp;rsquo;s the presence of &lt;code&gt;asyncio.sleep()&lt;/code&gt; in the example that is designed to mimic an IO-bound process where there is uncertain wait time involved.  For example, the &lt;code&gt;asyncio.sleep()&lt;/code&gt; call might represent sending and receiving not-so-random integers between two clients in a message application.&lt;/p&gt;
&lt;h2 id=&quot;async-io-design-patterns&quot;&gt;Async IO Design Patterns&lt;/h2&gt;
&lt;p&gt;Async IO comes with its own set of possible script designs, which you&amp;rsquo;ll get introduced to in this section.&lt;/p&gt;
&lt;h3 id=&quot;chaining-coroutines&quot;&gt;Chaining Coroutines&lt;/h3&gt;
&lt;p&gt;A key feature of coroutines is that they can be chained together.  (Remember, a coroutine object is awaitable, so another coroutine can &lt;code&gt;await&lt;/code&gt; it.)  This allows you to break programs into smaller, manageable, recyclable coroutine:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# chained.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&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;time&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;randint&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;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;b&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;o&quot;&gt;-&amp;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;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;randint&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;part1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&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;o&quot;&gt;-&amp;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;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&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;0&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;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;part1(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) sleeping for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&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;s2&quot;&gt;&amp;quot;result&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-1&amp;quot;&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;Returning part1(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) == &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{result}&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;part2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&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;arg&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;o&quot;&gt;-&amp;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;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&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;0&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;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;part2{n, arg} sleeping for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&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;s2&quot;&gt;&amp;quot;result&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-2 derived from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{arg}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&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;Returning part2{n, arg} == &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{result}&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&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;o&quot;&gt;-&amp;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;start&lt;/span&gt; &lt;span class=&quot;o&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;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p1&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;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&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;start&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;--&amp;gt;Chained result&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; =&amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{p2}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (took &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{end:0.2f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds).&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&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;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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&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;n&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&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;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&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;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;444&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;mi&quot;&gt;2&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;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;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&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;nb&quot;&gt;int&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;mi&quot;&gt;1&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;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&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;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;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&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;perf_counter&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;start&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;Program finished in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{end:0.2f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pay careful attention to the output, where &lt;code&gt;part1()&lt;/code&gt; sleeps for a variable amount of time, and &lt;code&gt;part2()&lt;/code&gt; begins working with the results as they become 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; python3 chained.py &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;part1(9) sleeping for 4 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;part1(6) sleeping for 4 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;part1(3) sleeping for 0 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Returning part1(3) == result3-1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;part2(3, &amp;#39;result3-1&amp;#39;) sleeping for 4 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Returning part1(9) == result9-1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;part2(9, &amp;#39;result9-1&amp;#39;) sleeping for 7 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Returning part1(6) == result6-1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;part2(6, &amp;#39;result6-1&amp;#39;) sleeping for 4 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Returning part2(3, &amp;#39;result3-1&amp;#39;) == result3-2 derived from result3-1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--&amp;gt;Chained result3 =&amp;gt; result3-2 derived from result3-1 (took 4.00 seconds).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Returning part2(6, &amp;#39;result6-1&amp;#39;) == result6-2 derived from result6-1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--&amp;gt;Chained result6 =&amp;gt; result6-2 derived from result6-1 (took 8.01 seconds).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Returning part2(9, &amp;#39;result9-1&amp;#39;) == result9-2 derived from result9-1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--&amp;gt;Chained result9 =&amp;gt; result9-2 derived from result9-1 (took 11.01 seconds).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Program finished in 11.01 seconds.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this setup, the runtime of &lt;code&gt;main()&lt;/code&gt; will be equal to the maximum runtime of the tasks that it gathers together and schedules.&lt;/p&gt;
&lt;h3 id=&quot;using-a-queue&quot;&gt;Using a Queue&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;asyncio&lt;/code&gt; package provides &lt;a href=&quot;https://docs.python.org/3/library/asyncio-queue.html&quot;&gt;queue classes&lt;/a&gt; that are designed to be similar to classes of the &lt;a href=&quot;https://docs.python.org/3/library/queue.html#module-queue&quot;&gt;&lt;code&gt;queue&lt;/code&gt;&lt;/a&gt; module.  In our examples so far, we haven&amp;rsquo;t really had a need for a queue structure.  In &lt;code&gt;chained.py&lt;/code&gt;, each task (future) is composed of a set of coroutines that explicitly await each other and pass through a single input per chain.&lt;/p&gt;
&lt;p&gt;There is an alternative structure that can also work with async IO: a number of producers, which are not associated with each other, add items to a queue.  Each producer may add multiple items to the queue at staggered, random, unannounced times.  A group of consumers pull items from the queue as they show up, greedily and without waiting for any other signal.&lt;/p&gt;
&lt;p&gt;In this design, there is no chaining of any individual consumer to a producer.  The consumers don&amp;rsquo;t know the number of producers, or even the cumulative number of items that will be added to the queue, in advance.&lt;/p&gt;
&lt;p&gt;It takes an individual producer or consumer a variable amount of time to put and extract items from the queue, respectively.  The queue serves as a throughput that can communicate with the producers and consumers without them talking to each other directly.&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;: While queues are often used in threaded programs because of the thread-safety of &lt;code&gt;queue.Queue()&lt;/code&gt;, you shouldn&amp;rsquo;t need to concern yourself with thread safety when it comes to async IO.  (The exception is when you&amp;rsquo;re combining the two, but that isn&amp;rsquo;t done in this tutorial.)&lt;/p&gt;
&lt;p&gt;One use-case for queues (as is the case here) is for the queue to act as a transmitter for producers and consumers that aren&amp;rsquo;t otherwise directly chained or associated with each other.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The synchronous version of this program would look pretty dismal: a group of blocking producers serially add items to the queue, one producer at a time.  Only after all producers are done can the queue be processed, by one consumer at a time processing item-by-item.  There is a ton of latency in this design.  Items may sit idly in the queue rather than be picked up and processed immediately.&lt;/p&gt;
&lt;p&gt;An asynchronous version, &lt;code&gt;asyncq.py&lt;/code&gt;, is below.  The challenging part of this workflow is that there needs to be a signal to the consumers that production is done.  Otherwise, &lt;code&gt;await q.get()&lt;/code&gt; will hang indefinitely, because the queue will have been fully processed, but consumers won&amp;rsquo;t have any idea that production is complete.&lt;/p&gt;
&lt;p&gt;(Big thanks for some help from a StackOverflow &lt;a href=&quot;https://stackoverflow.com/a/52615705/7954504&quot;&gt;user&lt;/a&gt; for helping to straighten out &lt;code&gt;main()&lt;/code&gt;: the key is to &lt;code&gt;await q.join()&lt;/code&gt;, which blocks until all items in the queue have been received and processed, and then to cancel the consumer tasks, which would otherwise hang up and wait endlessly for additional queue items to appear.)&lt;/p&gt;
&lt;p&gt;Here is the full script:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# asyncq.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&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;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;it&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&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;time&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;makeitem&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;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&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;o&quot;&gt;-&amp;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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urandom&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;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;randint&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;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;b&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;o&quot;&gt;-&amp;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;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;randint&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;randsleep&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;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&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;b&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;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;caller&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;o&quot;&gt;-&amp;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;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&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;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&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;caller&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;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{caller}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; sleeping for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;produce&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;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;o&quot;&gt;-&amp;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;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&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;5&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;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;p&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;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Synchronous loop for each single producer&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randsleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caller&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;s2&quot;&gt;&amp;quot;Producer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&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;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;makeitem&lt;/span&gt;&lt;span class=&quot;p&quot;&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;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&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;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&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;s2&quot;&gt;&amp;quot;Producer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; added &amp;lt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;gt; to queue.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consume&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;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;o&quot;&gt;-&amp;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;k&quot;&gt;while&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;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randsleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caller&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;s2&quot;&gt;&amp;quot;Consumer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&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;i&lt;/span&gt;&lt;span class=&quot;p&quot;&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&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;now&lt;/span&gt; &lt;span class=&quot;o&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;perf_counter&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;s2&quot;&gt;&amp;quot;Consumer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; got element &amp;lt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;gt;&amp;quot;&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; in {now-t:0.5f} seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task_done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&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;n&quot;&gt;nprod&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;ncon&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;q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;producers&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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;produce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&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;n&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;n&quot;&gt;nprod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;consumers&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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&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;n&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;n&quot;&gt;ncon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&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;producers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&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;c1&quot;&gt;# Implicitly awaits consumers, too&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consumers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancel&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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;argparse&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;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;444&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argparse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;-p&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;--nprod&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&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;default&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;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;-c&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;--ncon&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&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;default&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;ns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_args&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;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&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;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__dict__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;o&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;perf_counter&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;start&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;Program completed in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{elapsed:0.5f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first few coroutines are helper functions that return a random string, a fractional-second performance counter, and a random integer.  A producer puts anywhere from 1 to 5 items into the queue.  Each item is a tuple of &lt;code&gt;(i, t)&lt;/code&gt; where &lt;code&gt;i&lt;/code&gt; is a random string and &lt;code&gt;t&lt;/code&gt; is the time at which the producer attempts to put the tuple into the queue.&lt;/p&gt;
&lt;p&gt;When a consumer pulls an item out, it simply calculates the elapsed time that the item sat in the queue using the timestamp that the item was put in with.&lt;/p&gt;
&lt;p&gt;Keep in mind that &lt;code&gt;asyncio.sleep()&lt;/code&gt; is used to mimic some other, more complex coroutine that would eat up time and block all other execution if it were a regular blocking function.&lt;/p&gt;
&lt;p&gt;Here is a test run with two producers and five consumers:&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 asyncq.py -p &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; -c &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer 0 sleeping for 3 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer 1 sleeping for 3 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 0 sleeping for 4 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 1 sleeping for 3 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 2 sleeping for 3 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 3 sleeping for 5 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 4 sleeping for 4 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer 0 added &amp;lt;377b1e8f82&amp;gt; to queue.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer 0 sleeping for 5 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer 1 added &amp;lt;413b8802f8&amp;gt; to queue.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 1 got element &amp;lt;377b1e8f82&amp;gt; in 0.00013 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 1 sleeping for 3 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 2 got element &amp;lt;413b8802f8&amp;gt; in 0.00009 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 2 sleeping for 4 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer 0 added &amp;lt;06c055b3ab&amp;gt; to queue.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer 0 sleeping for 1 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 0 got element &amp;lt;06c055b3ab&amp;gt; in 0.00021 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 0 sleeping for 4 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer 0 added &amp;lt;17a8613276&amp;gt; to queue.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 4 got element &amp;lt;17a8613276&amp;gt; in 0.00022 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer 4 sleeping for 5 seconds.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Program completed in 9.00954 seconds.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, the items process in fractions of a second.  A delay can be due to two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Standard, largely unavoidable overhead&lt;/li&gt;
&lt;li&gt;Situations where all consumers are sleeping when an item appears in the queue&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With regards to the second reason, luckily, it is perfectly normal to scale to hundreds or thousands of consumers.  You should have no problem with &lt;code&gt;python3 asyncq.py -p 5 -c 100&lt;/code&gt;.  The point here is that, theoretically, you could have different users on different systems controlling the management of producers and consumers, with the queue serving as the central throughput.&lt;/p&gt;
&lt;p&gt;So far, you&amp;rsquo;ve been thrown right into the fire and seen three related examples of &lt;code&gt;asyncio&lt;/code&gt; calling coroutines defined with &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;.  If you&amp;rsquo;re not completely following or just want to get deeper into the mechanics of how modern coroutines came to be in Python, you&amp;rsquo;ll start from square one with the next section.&lt;/p&gt;
&lt;h2 id=&quot;async-ios-roots-in-generators&quot;&gt;Async IO&amp;rsquo;s Roots in Generators&lt;/h2&gt;
&lt;p&gt;Earlier, you saw an example of the old-style generator-based coroutines, which have been outdated by more explicit native coroutines.  The example is worth re-showing with a small tweak:&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;asyncio&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coroutine&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;py34_coro&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;Generator-based coroutine&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# No need to build these yourself, but be aware of what they are&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;k&quot;&gt;yield from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stuff&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;s&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;py35_coro&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;Native coroutine, modern syntax&amp;quot;&amp;quot;&amp;quot;&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stuff&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;s&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;stuff&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;mh&quot;&gt;0x10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x30&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As an experiment, what happens if you call &lt;code&gt;py34_coro()&lt;/code&gt; or &lt;code&gt;py35_coro()&lt;/code&gt; on its own, without &lt;code&gt;await&lt;/code&gt;, or without any calls to &lt;code&gt;asyncio.run()&lt;/code&gt; or other &lt;code&gt;asyncio&lt;/code&gt; &amp;ldquo;porcelain&amp;rdquo; functions?  Calling a coroutine in isolation returns a coroutine 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;py35_coro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;coroutine object py35_coro at 0x10126dcc8&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This isn&amp;rsquo;t very interesting on its surface.  The result of calling a coroutine on its own is an awaitable &lt;strong&gt;coroutine object&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Time for a quiz: what other feature of Python looks like this?  (What feature of Python doesn&amp;rsquo;t actually &amp;ldquo;do much&amp;rdquo; when it&amp;rsquo;s called on its own?)&lt;/p&gt;
&lt;p&gt;Hopefully you&amp;rsquo;re thinking of &lt;strong&gt;generators&lt;/strong&gt; as an answer to this question, because coroutines are enhanced generators under the hood.  The behavior is similar in this regard:&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;gen&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;yield&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x30&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;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gen&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;g&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Nothing much happens - need to iterate with `.__next__()`&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;generator object gen at 0x1012705e8&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;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(16, 32, 48)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Generator functions are, as it so happens, the foundation of async IO (regardless of whether you declare coroutines with &lt;code&gt;async def&lt;/code&gt; rather than the older &lt;code&gt;@asyncio.coroutine&lt;/code&gt; wrapper).  Technically, &lt;code&gt;await&lt;/code&gt; is more closely analogous to &lt;code&gt;yield from&lt;/code&gt; than it is to &lt;code&gt;yield&lt;/code&gt;.  (But remember that &lt;code&gt;yield from x()&lt;/code&gt; is just syntactic sugar to replace &lt;code&gt;for i in x(): yield i&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;One critical feature of generators as it pertains to async IO is that they can effectively be stopped and restarted at will.  For example, you can &lt;code&gt;break&lt;/code&gt; out of iterating over a generator object and then resume iteration on the remaining values later.  When a generator function reaches &lt;code&gt;yield&lt;/code&gt;, it yields that value, but then it sits idle until it is told to yield its subsequent value.&lt;/p&gt;
&lt;p&gt;This can be fleshed out through an example:&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;itertools&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cycle&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;endless&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Yields 9, 8, 7, 6, 9, 8, 7, 6, ... forever&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;yield from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cycle&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;8&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;6&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;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endless&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;total&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;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;n&quot;&gt;e&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;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;30&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;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;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;total&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;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;nb&quot;&gt;print&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;c1&quot;&gt;# Pause execution. We can resume later.&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;9 8 7 6 9 8 7 6 9 8 7 6 9 8&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;# Resume&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;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(6, 9, 8)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;await&lt;/code&gt; keyword behaves similarly, marking a break point at which the coroutine suspends itself and lets other coroutines work.  &amp;ldquo;Suspended,&amp;rdquo; in this case, means a coroutine that has temporarily ceded control but not totally exited or finished.  Keep in mind that &lt;code&gt;yield&lt;/code&gt;, and by extension &lt;code&gt;yield from&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;, mark a break point in a generator&amp;rsquo;s execution.&lt;/p&gt;
&lt;p&gt;This is the fundamental difference between functions and generators.  A function is all-or-nothing.  Once it starts, it won&amp;rsquo;t stop until it hits a &lt;code&gt;return&lt;/code&gt;, then pushes that value to the caller (the function that calls it).  A generator, on the other hand, pauses each time it hits a &lt;code&gt;yield&lt;/code&gt; and goes no further.  Not only can it push this value to calling stack, but it can keep a hold of its local variables when you resume it by calling &lt;code&gt;next()&lt;/code&gt; on it.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a second and lesser-known feature of generators that also matters. You can send a value into a generator as well through its &lt;code&gt;.send()&lt;/code&gt; method. This allows generators (and coroutines) to call (&lt;code&gt;await&lt;/code&gt;) each other without blocking.  I won&amp;rsquo;t get any further into the nuts and bolts of this feature, because it matters mainly for the implementation of coroutines behind the scenes, but you shouldn&amp;rsquo;t ever really need to use it directly yourself.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in exploring more, you can start at &lt;a href=&quot;https://www.python.org/dev/peps/pep-0342/&quot;&gt;PEP 342&lt;/a&gt;, where coroutines were formally introduced.  Brett Cannon&amp;rsquo;s &lt;a href=&quot;https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/&quot;&gt;How the Heck Does Async-Await Work in Python&lt;/a&gt; is also a good read, as is the &lt;a href=&quot;https://pymotw.com/3/asyncio/coroutines.html&quot;&gt;PYMOTW writeup on &lt;code&gt;asyncio&lt;/code&gt;&lt;/a&gt;.  Lastly, there&amp;rsquo;s David Beazley&amp;rsquo;s &lt;a href=&quot;http://www.dabeaz.com/coroutines/&quot;&gt;Curious Course on Coroutines and Concurrency&lt;/a&gt;, which dives deep into the mechanism by which coroutines run.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try to condense all of the above articles into a few sentences: there is a particularly unconventional mechanism by which these coroutines actually get run. Their result is an attribute of the exception object that gets thrown when their &lt;code&gt;.send()&lt;/code&gt; method is called.  There&amp;rsquo;s some more wonky detail to all of this, but it probably won&amp;rsquo;t help you use this part of the language in practice, so let&amp;rsquo;s move on for now.&lt;/p&gt;
&lt;p&gt;To tie things together, here are some key points on the topic of coroutines as generators:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Coroutines are &lt;a href=&quot;https://www.python.org/dev/peps/pep-0492/#differences-from-generators&quot;&gt;repurposed generators&lt;/a&gt; that take advantage of the peculiarities of generator methods.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Old generator-based coroutines use &lt;code&gt;yield from&lt;/code&gt; to wait for a coroutine result.  Modern Python syntax in native coroutines simply replaces &lt;code&gt;yield from&lt;/code&gt; with &lt;code&gt;await&lt;/code&gt; as the means of waiting on a coroutine result.  The &lt;code&gt;await&lt;/code&gt; is analogous to &lt;code&gt;yield from&lt;/code&gt;, and it often helps to think of it as such.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The use of &lt;code&gt;await&lt;/code&gt; is a signal that marks a break point.  It lets a coroutine temporarily suspend execution and permits the program to come back to it later.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;other-features-async-for-and-async-generators-comprehensions&quot;&gt;Other Features: &lt;code&gt;async for&lt;/code&gt; and Async Generators + Comprehensions&lt;/h3&gt;
&lt;p&gt;Along with plain &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, Python also enables &lt;code&gt;async for&lt;/code&gt; to iterate over an &lt;strong&gt;asynchronous iterator&lt;/strong&gt;.  The purpose of an asynchronous iterator is for it to be able to call asynchronous code at each stage when it is iterated over.&lt;/p&gt;
&lt;p&gt;A natural extension of this concept is an &lt;strong&gt;asynchronous generator&lt;/strong&gt;.  Recall that you can use &lt;code&gt;await&lt;/code&gt;, &lt;code&gt;return&lt;/code&gt;, or &lt;code&gt;yield&lt;/code&gt; in a native coroutine.  Using &lt;code&gt;yield&lt;/code&gt; within a coroutine became possible in Python 3.6 (via PEP 525), which introduced asynchronous generators with the purpose of allowing &lt;code&gt;await&lt;/code&gt; and &lt;code&gt;yield&lt;/code&gt; to be used in the same coroutine function body:&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;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mygen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&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;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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Yield powers of 2.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;gp&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;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&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;yield&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&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;gp&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;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Last but not least, Python enables &lt;strong&gt;asynchronous comprehension&lt;/strong&gt; with &lt;code&gt;async for&lt;/code&gt;.  Like its synchronous cousin, this is largely syntactic sugar:&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;async&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# This does *not* introduce concurrent execution&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# It is meant to show syntax only&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;g&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;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&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;n&quot;&gt;mygen&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;f&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;j&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mygen&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&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;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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&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;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;g&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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&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;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]&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;f&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 2, 16, 32, 256, 512]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a crucial distinction: &lt;strong&gt;neither asynchronous generators nor comprehensions make the iteration concurrent&lt;/strong&gt;.  All that they do is provide the look-and-feel of their synchronous counterparts, but with the ability for the loop in question to give up control to the event loop for some other coroutine to run.&lt;/p&gt;
&lt;p&gt;In other words, asynchronous iterators and asynchronous generators are not designed to concurrently map some function over a sequence or iterator.  They&amp;rsquo;re merely designed to let the enclosing coroutine allow other tasks to take their turn.  The &lt;code&gt;async for&lt;/code&gt; and &lt;code&gt;async with&lt;/code&gt; statements are only needed to the extent that using plain &lt;code&gt;for&lt;/code&gt; or &lt;code&gt;with&lt;/code&gt; would &amp;ldquo;break&amp;rdquo; the nature of &lt;code&gt;await&lt;/code&gt; in the coroutine.  This distinction between asynchronicity and concurrency is a key one to grasp.&lt;/p&gt;
&lt;h3 id=&quot;the-event-loop-and-asynciorun&quot;&gt;The Event Loop and &lt;code&gt;asyncio.run()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;You can think of an event loop as something like a &lt;code&gt;while True&lt;/code&gt; loop that monitors coroutines, taking feedback on what&amp;rsquo;s idle, and looking around for things that can be executed in the meantime.  It is able to wake up an idle coroutine when whatever that coroutine is waiting on becomes available.&lt;/p&gt;
&lt;p&gt;Thus far, the entire management of the event loop has been implicitly handled by one function 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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&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;span class=&quot;c1&quot;&gt;# Python 3.7+&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/python/cpython/blob/d4c76d960b8b286b75c933780416ace9cda682fd/Lib/asyncio/runners.py#L8&quot;&gt;&lt;code&gt;asyncio.run()&lt;/code&gt;&lt;/a&gt;, introduced in Python 3.7, is responsible for getting the event loop, running tasks until they are marked as complete, and then closing the event loop.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a more long-winded way of managing the &lt;code&gt;asyncio&lt;/code&gt; event loop, with &lt;code&gt;get_event_loop()&lt;/code&gt;.  The typical pattern looks 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;n&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_event_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run_until_complete&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;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loop&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;You&amp;rsquo;ll probably see &lt;code&gt;loop.get_event_loop()&lt;/code&gt; floating around in older examples, but unless you have a specific need to fine-tune control over the event loop management, &lt;code&gt;asyncio.run()&lt;/code&gt; should be sufficient for most programs.&lt;/p&gt;
&lt;p&gt;If you do need to interact with the event loop within a Python program, &lt;code&gt;loop&lt;/code&gt; is a good-old-fashioned Python object that supports introspection with &lt;code&gt;loop.is_running()&lt;/code&gt; and &lt;code&gt;loop.is_closed()&lt;/code&gt;.  You can manipulate it if you need to get more fine-tuned control, such as in &lt;a href=&quot;https://docs.python.org/3/library/asyncio-eventloop.html#asyncio-example-lowlevel-helloworld&quot;&gt;scheduling a callback&lt;/a&gt; by passing the loop as an argument.&lt;/p&gt;
&lt;p&gt;What is more crucial is understanding a bit beneath the surface about the mechanics of the event loop.  Here are a few points worth stressing about the event loop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;#1:&lt;/strong&gt; Coroutines don&amp;rsquo;t do much on their own until they are tied to the event loop.&lt;/p&gt;
&lt;p&gt;You saw this point before in the explanation on generators, but it&amp;rsquo;s worth restating.  If you have a main coroutine that awaits others, simply calling it in isolation has little effect:&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;asyncio&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;async&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;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;s2&quot;&gt;&amp;quot;Hello ...&amp;quot;&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;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;1&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;s2&quot;&gt;&amp;quot;World!&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;routine&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;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;routine&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;coroutine object main at 0x1027a6150&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Remember to use &lt;code&gt;asyncio.run()&lt;/code&gt; to actually force execution by scheduling the &lt;code&gt;main()&lt;/code&gt; coroutine (future object) for execution on the event loop:&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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;routine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Hello ...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;World!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(Other coroutines can be executed with &lt;code&gt;await&lt;/code&gt;.  It is typical to wrap just &lt;code&gt;main()&lt;/code&gt; in &lt;code&gt;asyncio.run()&lt;/code&gt;, and chained coroutines with &lt;code&gt;await&lt;/code&gt; will be called from there.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;#2:&lt;/strong&gt; By default, an async IO event loop runs in a single thread and on a single CPU core.  Usually, running one single-threaded event loop in one CPU core is more than sufficient.  It is also possible to run event loops across multiple cores.  Check out this &lt;a href=&quot;https://youtu.be/0kXaLh8Fz3k?t=10m30s&quot;&gt;talk by John Reese&lt;/a&gt; for more, and be warned that your laptop may spontaneously combust.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Event loops are pluggable.  That is, you could, if you really wanted, write your own event loop implementation and have it run tasks just the same.  This is wonderfully demonstrated in the &lt;a href=&quot;https://github.com/MagicStack/uvloop&quot;&gt;&lt;code&gt;uvloop&lt;/code&gt;&lt;/a&gt; package, which is an implementation of the event loop in Cython.&lt;/p&gt;
&lt;p&gt;That is what is meant by the term &amp;ldquo;pluggable event loop&amp;rdquo;: you can use any working implementation of an event loop, unrelated to the structure of the coroutines themselves.  The &lt;code&gt;asyncio&lt;/code&gt; package itself ships with &lt;a href=&quot;https://docs.python.org/3/library/asyncio-eventloop.html#event-loop-implementations&quot;&gt;two different event loop implementations&lt;/a&gt;, with the default being based on the &lt;a href=&quot;https://docs.python.org/3/library/selectors.html#module-selectors&quot;&gt;&lt;code&gt;selectors&lt;/code&gt;&lt;/a&gt; module.  (The second implementation is built for Windows only.)&lt;/p&gt;
&lt;h2 id=&quot;a-full-program-asynchronous-requests&quot;&gt;A Full Program: Asynchronous Requests&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve made it this far, and now it&amp;rsquo;s time for the fun and painless part.  In this section, you&amp;rsquo;ll build a web-scraping URL collector, &lt;code&gt;areq.py&lt;/code&gt;, using &lt;code&gt;aiohttp&lt;/code&gt;, a blazingly fast async HTTP client/server framework.  (We just need the client part.)  Such a tool could be used to map connections between a cluster of sites, with the links forming a &lt;a href=&quot;https://en.wikipedia.org/wiki/Directed_graph&quot;&gt;directed graph&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;: You may be wondering why Python&amp;rsquo;s &lt;code&gt;requests&lt;/code&gt; package isn&amp;rsquo;t compatible with async IO.  &lt;code&gt;requests&lt;/code&gt; is built on top of &lt;code&gt;urrlib3&lt;/code&gt;, which in turn uses Python&amp;rsquo;s &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;socket&lt;/code&gt; modules.&lt;/p&gt;
&lt;p&gt;By default, socket operations are blocking.  This means that Python won&amp;rsquo;t like &lt;code&gt;await requests.get(url)&lt;/code&gt; because &lt;code&gt;.get()&lt;/code&gt; is not awaitable.  In contrast, almost everything in &lt;code&gt;aiohttp&lt;/code&gt; is an awaitable coroutine, such as &lt;a href=&quot;https://github.com/aio-libs/aiohttp/blob/508adbb656da2e9ae660da5e98e1e5fa6669a3f4/aiohttp/client.py#L225&quot;&gt;&lt;code&gt;session.request()&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/aio-libs/aiohttp/blob/da75122f6089a250128d2736f2bd88d10e97ca17/aiohttp/client_reqrep.py#L913&quot;&gt;&lt;code&gt;response.text()&lt;/code&gt;&lt;/a&gt;.  It&amp;rsquo;s a great package otherwise, but you&amp;rsquo;re doing yourself a disservice by using &lt;code&gt;requests&lt;/code&gt; in asynchronous code.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The high-level program structure will look like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Read a sequence of URLs from a local file, &lt;code&gt;urls.txt&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Send GET requests for the URLs and decode the resulting content.  If this fails, stop there for a URL.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search for the URLs within &lt;code&gt;href&lt;/code&gt; tags in the HTML of the responses.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Write the results to &lt;code&gt;foundurls.txt&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do all of the above as asynchronously and concurrently as possible.  (Use &lt;code&gt;aiohttp&lt;/code&gt; for the requests, and &lt;code&gt;aiofiles&lt;/code&gt; for the file-appends.  These are two primary examples of IO that are well-suited for the async IO model.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here are the contents of &lt;code&gt;urls.txt&lt;/code&gt;.  It&amp;rsquo;s not huge, and contains mostly highly trafficked sites:&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 urls.txt
&lt;span class=&quot;go&quot;&gt;https://regex101.com/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://docs.python.org/3/this-url-will-404.html&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://www.nytimes.com/guides/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://www.mediamatters.org/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://1.1.1.1/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://www.politico.com/tipsheets/morning-money&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://www.bloomberg.com/markets/economics&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://www.ietf.org/rfc/rfc2616.txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The second URL in the list should return a 404 response, which you&amp;rsquo;ll need to handle gracefully.  If you&amp;rsquo;re running an expanded version of this program, you&amp;rsquo;ll probably need to deal with much hairier problems than this, such a server disconnections and endless redirects.&lt;/p&gt;
&lt;p&gt;The requests themselves should be made using a single session, to take advantage of reusage of the session&amp;rsquo;s internal connection pool.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at the full program.  We&amp;rsquo;ll walk through things step-by-step after:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# areq.py&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Asynchronously get links embedded in multiple pages&amp;#39; HMTL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&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;re&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;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;IO&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.error&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.parse&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;aiofiles&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;aiohttp&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;aiohttp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientSession&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;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;%(levelname)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(name)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;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;DEBUG&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;stream&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;stderr&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;logger&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;getLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;areq&amp;quot;&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;getLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;chardet.charsetprober&amp;quot;&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;disabled&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;n&quot;&gt;HREF_RE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;href=&amp;quot;(.*?)&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch_html&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;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientSession&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;span class=&quot;o&quot;&gt;-&amp;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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;GET request wrapper to fetch page HTML.&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;    kwargs are passed to `session.request()`.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;GET&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;n&quot;&gt;url&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;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raise_for_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logger&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;Got response [&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;] for URL: &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;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&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;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&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;html&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse&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;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientSession&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;span class=&quot;o&quot;&gt;-&amp;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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Find HREFs in the HTML of `url`.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;found&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;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_html&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;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&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;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;aiohttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;aiohttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http_exceptions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpProcessingError&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;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&amp;quot;aiohttp exception for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;]: &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;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&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;nb&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&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;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;found&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&amp;quot;Non-aiohttp exception occured:  &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;nb&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__dict__&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;found&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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HREF_RE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;abslink&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urllib&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urljoin&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;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urllib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URLError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&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;logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Error parsing URL: &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;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&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;n&quot;&gt;found&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;abslink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logger&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;Found &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; links for &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;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;found&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;found&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;write_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;IO&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;nb&quot;&gt;str&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;span class=&quot;o&quot;&gt;-&amp;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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Write the found HREFs from `url` to `file`.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&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;url&lt;/span&gt;&lt;span class=&quot;o&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;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;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;res&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;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aiofiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;s2&quot;&gt;&amp;quot;a&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;f&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;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&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;write&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;{url}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{p}&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;n&quot;&gt;logger&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;Wrote results for source URL: &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;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bulk_crawl_and_write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&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;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;span class=&quot;o&quot;&gt;-&amp;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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Crawl &amp;amp; write concurrently to `file` for multiple `urls`.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientSession&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;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tasks&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;url&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tasks&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;write_one&lt;/span&gt;&lt;span class=&quot;p&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;file&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;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&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;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&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;tasks&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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pathlib&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;assert&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;version_info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&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;s2&quot;&gt;&amp;quot;Script requires Python 3.7+.&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;here&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathlib&lt;/span&gt;&lt;span class=&quot;o&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;vm&quot;&gt;__file__&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;parent&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;here&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;joinpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;urls.txt&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;infile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;urls&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;nb&quot;&gt;map&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;outpath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;here&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;joinpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;foundurls.txt&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;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;w&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;outfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;outfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;source_url&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;parsed_url&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;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bulk_crawl_and_write&lt;/span&gt;&lt;span class=&quot;p&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;outpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This script is longer than our initial toy programs, so let&amp;rsquo;s break it down.&lt;/p&gt;
&lt;p&gt;The constant &lt;code&gt;HREF_RE&lt;/code&gt; is a regular expression to extract what we&amp;rsquo;re ultimately searching for, &lt;code&gt;href&lt;/code&gt; tags within HTML:&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;HREF_RE&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;s1&quot;&gt;&amp;#39;Go to &amp;lt;a href=&amp;quot;https://realpython.com/&amp;quot;&amp;gt;Real Python&amp;lt;/a&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;re.Match object; span=(15, 45), match=&amp;#39;href=&amp;quot;https://realpython.com/&amp;quot;&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The coroutine &lt;code&gt;fetch_html()&lt;/code&gt; is a wrapper around a GET request to make the request and decode the resulting page HTML.  It makes the request, awaits the response, and raises right away in the case of a non-200 status:&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;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;GET&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;n&quot;&gt;url&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;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raise_for_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the status is okay, &lt;code&gt;fetch_html()&lt;/code&gt; returns the page HTML (a &lt;code&gt;str&lt;/code&gt;).  Notably, there is no exception handling done in this function.  The logic is to propagate that exception to the caller and let it be handled there:&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;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We &lt;code&gt;await&lt;/code&gt; &lt;code&gt;session.request()&lt;/code&gt; and &lt;code&gt;resp.text()&lt;/code&gt; because they&amp;rsquo;re awaitable coroutines.  The request/response cycle would otherwise be the long-tailed, time-hogging portion of the application, but with async IO, &lt;code&gt;fetch_html()&lt;/code&gt; lets the event loop work on other readily available jobs such as parsing and writing URLs that have already been fetched.&lt;/p&gt;
&lt;p&gt;Next in the chain of coroutines comes &lt;code&gt;parse()&lt;/code&gt;, which waits on &lt;code&gt;fetch_html()&lt;/code&gt; for a given URL, and then extracts all of the &lt;code&gt;href&lt;/code&gt; tags from that page&amp;rsquo;s HTML, making sure that each is valid and formatting it as an absolute path.&lt;/p&gt;
&lt;p&gt;Admittedly, the second portion of &lt;code&gt;parse()&lt;/code&gt; is blocking, but it consists of a quick regex match and ensuring that the links discovered are made into absolute paths.&lt;/p&gt;
&lt;p&gt;In this specific case, this synchronous code should be quick and inconspicuous.  But just remember that any line within a given coroutine will block other coroutines unless that line uses &lt;code&gt;yield&lt;/code&gt;, &lt;code&gt;await&lt;/code&gt;, or &lt;code&gt;return&lt;/code&gt;.  If the parsing was a more intensive process, you might want to consider running this portion in its own process with &lt;a href=&quot;https://docs.python.org/3/library/asyncio-eventloop.html#executing-code-in-thread-or-process-pools&quot;&gt;&lt;code&gt;loop.run_in_executor()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, the coroutine &lt;code&gt;write()&lt;/code&gt; takes a file object and a single URL, and waits on &lt;code&gt;parse()&lt;/code&gt; to return a &lt;code&gt;set&lt;/code&gt; of the parsed URLs, writing each to the file asynchronously along with its source URL through use of &lt;code&gt;aiofiles&lt;/code&gt;, a package for async file IO.&lt;/p&gt;
&lt;p&gt;Lastly, &lt;code&gt;bulk_crawl_and_write()&lt;/code&gt; serves as the main entry point into the script&amp;rsquo;s chain of coroutines.  It uses a single session, and a task is created for each URL that is ultimately read from &lt;code&gt;urls.txt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here are a few additional points that deserve mention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The default &lt;code&gt;ClientSession&lt;/code&gt; has an &lt;a href=&quot;https://aiohttp.readthedocs.io/en/stable/client_reference.html#connectors&quot;&gt;adapter&lt;/a&gt; with a maximum of 100 open connections.  To change that, pass an instance of &lt;code&gt;asyncio.connector.TCPConnector&lt;/code&gt; to &lt;code&gt;ClientSession&lt;/code&gt;.  You can also specify limits on a per-host basis.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can specify max &lt;a href=&quot;https://aiohttp.readthedocs.io/en/stable/client_quickstart.html#timeouts&quot;&gt;timeouts&lt;/a&gt; for both the session as a whole and for individual requests.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This script also uses &lt;code&gt;async with&lt;/code&gt;, which works with an &lt;a href=&quot;https://www.python.org/dev/peps/pep-0492/#asynchronous-context-managers-and-async-with&quot;&gt;asynchronous context manager&lt;/a&gt;.  I haven&amp;rsquo;t devoted a whole section to this concept because the transition from synchronous to asynchronous context managers is fairly straightforward.  The latter has to define &lt;code&gt;.__aenter__()&lt;/code&gt; and &lt;code&gt;.__aexit__()&lt;/code&gt; rather than &lt;code&gt;.__exit__()&lt;/code&gt; and &lt;code&gt;.__enter__()&lt;/code&gt;.  As you might expect, &lt;code&gt;async with&lt;/code&gt; can only be used inside a coroutine function declared with &lt;code&gt;async def&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;d like to explore a bit more, the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/asyncio-walkthrough&quot;&gt;companion files&lt;/a&gt; for this tutorial up at GitHub have comments and docstrings attached as well.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the execution in all of its glory, as &lt;code&gt;areq.py&lt;/code&gt; gets, parses, and saves results for 9 URLs in under a second:&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 areq.py
&lt;span class=&quot;go&quot;&gt;21:33:22 DEBUG:asyncio: Using selector: KqueueSelector&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Got response [200] for URL: https://www.mediamatters.org/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Found 115 links for https://www.mediamatters.org/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Got response [200] for URL: https://www.nytimes.com/guides/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Got response [200] for URL: https://www.politico.com/tipsheets/morning-money&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Got response [200] for URL: https://www.ietf.org/rfc/rfc2616.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 ERROR:areq: aiohttp exception for https://docs.python.org/3/this-url-will-404.html [404]: Not Found&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Found 120 links for https://www.nytimes.com/guides/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Found 143 links for https://www.politico.com/tipsheets/morning-money&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Wrote results for source URL: https://www.mediamatters.org/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Found 0 links for https://www.ietf.org/rfc/rfc2616.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Got response [200] for URL: https://1.1.1.1/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Wrote results for source URL: https://www.nytimes.com/guides/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Wrote results for source URL: https://www.politico.com/tipsheets/morning-money&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Got response [200] for URL: https://www.bloomberg.com/markets/economics&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Found 3 links for https://www.bloomberg.com/markets/economics&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:22 INFO:areq: Wrote results for source URL: https://www.bloomberg.com/markets/economics&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:23 INFO:areq: Found 36 links for https://1.1.1.1/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:23 INFO:areq: Got response [200] for URL: https://regex101.com/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:23 INFO:areq: Found 23 links for https://regex101.com/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:23 INFO:areq: Wrote results for source URL: https://regex101.com/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;21:33:23 INFO:areq: Wrote results for source URL: https://1.1.1.1/&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s not too shabby!  As a sanity check, you can check the line-count on the output.  In my case, it&amp;rsquo;s 626, though keep in mind this may fluctuate:&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 foundurls.txt
&lt;span class=&quot;go&quot;&gt;     626 foundurls.txt&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; head -n &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; foundurls.txt
&lt;span class=&quot;go&quot;&gt;source_url  parsed_url&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://www.bloomberg.com/markets/economics https://www.bloomberg.com/feedback&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://www.bloomberg.com/markets/economics https://www.bloomberg.com/notices/tos&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;Next Steps&lt;/strong&gt;: If you&amp;rsquo;d like to up the ante, make this webcrawler recursive.  You can use &lt;a href=&quot;https://github.com/aio-libs/aioredis&quot;&gt;&lt;code&gt;aio-redis&lt;/code&gt;&lt;/a&gt; to keep track of which URLs have been crawled within the tree to avoid requesting them twice, and connect links with Python&amp;rsquo;s &lt;code&gt;networkx&lt;/code&gt; library.&lt;/p&gt;
&lt;p&gt;Remember to be nice.  Sending 1000 concurrent requests to a small, unsuspecting website is bad, bad, bad.  There are ways to limit how many concurrent requests you&amp;rsquo;re making in one batch, such as in using the &lt;a href=&quot;https://stackoverflow.com/q/40836800/7954504&quot;&gt;sempahore&lt;/a&gt; objects of &lt;code&gt;asyncio&lt;/code&gt; or using a pattern &lt;a href=&quot;https://www.artificialworlds.net/blog/2017/05/31/python-3-large-numbers-of-tasks-with-limited-concurrency/&quot;&gt;like this one&lt;/a&gt;.  If you don&amp;rsquo;t heed this warning, you may get a massive batch of &lt;code&gt;TimeoutError&lt;/code&gt; exceptions and only end up hurting your own program.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;async-io-in-context&quot;&gt;Async IO in Context&lt;/h2&gt;
&lt;p&gt;Now that you&amp;rsquo;ve seen a healthy dose of code, let&amp;rsquo;s step back for a minute and consider when async IO is an ideal option and how you can make the comparison to arrive at that conclusion or otherwise choose a different model of concurrency.&lt;/p&gt;
&lt;h3 id=&quot;when-and-why-is-async-io-the-right-choice&quot;&gt;When and Why Is Async IO the Right Choice?&lt;/h3&gt;
&lt;p&gt;This tutorial is no place for an extended treatise on async IO versus threading versus multiprocessing.  However, it&amp;rsquo;s useful to have an idea of when async IO is probably the best candidate of the three.&lt;/p&gt;
&lt;p&gt;The battle over async IO versus multiprocessing is not really a battle at all.  In fact, they can be &lt;a href=&quot;https://youtu.be/0kXaLh8Fz3k?t=10m30s&quot;&gt;used in concert&lt;/a&gt;.  If you have multiple, fairly uniform CPU-bound tasks (a great example is a &lt;a href=&quot;http://scikit-learn.org/stable/modules/grid_search.html#parallelism&quot;&gt;grid search&lt;/a&gt; in libraries such as &lt;code&gt;scikit-learn&lt;/code&gt; or &lt;code&gt;keras&lt;/code&gt;), multiprocessing should be an obvious choice.&lt;/p&gt;
&lt;p&gt;Simply putting &lt;code&gt;async&lt;/code&gt; before every function is a bad idea if all of the functions use blocking calls.  (This can actually slow down your code.)  But as mentioned previously, there are places where async IO and multiprocessing can &lt;a href=&quot;https://youtu.be/0kXaLh8Fz3k?t=10m30s&quot;&gt;live in harmony&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The contest between async IO and threading is a little bit more direct.  I mentioned in the introduction that &amp;ldquo;threading is hard.&amp;rdquo;  The full story is that, even in cases where threading seems easy to implement, it can still lead to infamous impossible-to-trace bugs due to race conditions and memory usage, among other things.&lt;/p&gt;
&lt;p&gt;Threading also tends to scale less elegantly than async IO, because threads are a system resource with a finite availability.  Creating thousands of threads will fail on many machines, and I don&amp;rsquo;t recommend trying it in the first place.  Creating thousands of async IO tasks is completely feasible.&lt;/p&gt;
&lt;p&gt;Async IO shines when you have multiple IO-bound tasks where the tasks would otherwise be dominated by blocking IO-bound wait time, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Network IO, whether your program is the server or the client side&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Serverless designs, such as a peer-to-peer, multi-user network like a group chatroom&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Read/write operations where you want to mimic a &amp;ldquo;fire-and-forget&amp;rdquo; style but worry less about holding a lock on whatever you&amp;rsquo;re reading and writing to&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The biggest reason not to use it is that &lt;code&gt;await&lt;/code&gt; only supports a specific set of objects that define a specific set of methods.  If you want to do async read operations with a certain DBMS, you&amp;rsquo;ll need to find not just a Python wrapper for that DBMS, but one that supports the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; syntax.  Coroutines that contain synchronous calls block other coroutines and tasks from running.&lt;/p&gt;
&lt;p&gt;For a shortlist of libraries that work with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, see the &lt;a href=&quot;#libraries-that-work-with-asyncawait&quot;&gt;list&lt;/a&gt; at the end of this tutorial.&lt;/p&gt;
&lt;h3 id=&quot;async-io-it-is-but-which-one&quot;&gt;Async IO It Is, but Which One?&lt;/h3&gt;
&lt;p&gt;This tutorial focuses on async IO, the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; syntax, and using &lt;code&gt;asyncio&lt;/code&gt; for event-loop management and specifying tasks.  &lt;code&gt;asyncio&lt;/code&gt; certainly isn&amp;rsquo;t the only async IO library out there.  This observation from Nathaniel J. Smith says a lot:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[In] a few years, &lt;code&gt;asyncio&lt;/code&gt; might find itself relegated to becoming one of those stdlib libraries that savvy developers avoid, like &lt;code&gt;urllib2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;What I&amp;rsquo;m arguing, in effect, is that &lt;code&gt;asyncio&lt;/code&gt; is a victim of its own success: when it was designed, it used the best approach possible; but since then, work inspired by &lt;code&gt;asyncio&lt;/code&gt; – like the addition of &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; – has shifted the landscape so that we can do even better, and now &lt;code&gt;asyncio&lt;/code&gt; is hamstrung by its earlier commitments. &lt;a href=&quot;https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/&quot;&gt;(Source)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To that end, a few big-name alternatives that do what &lt;code&gt;asyncio&lt;/code&gt; does, albeit with different APIs and different approaches, are &lt;a href=&quot;https://github.com/dabeaz/curio&quot;&gt;&lt;code&gt;curio&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/python-trio/trio&quot;&gt;&lt;code&gt;trio&lt;/code&gt;&lt;/a&gt;.  Personally, I think that if you&amp;rsquo;re building a moderately sized, straightforward program, just using &lt;code&gt;asyncio&lt;/code&gt; is plenty sufficient and understandable, and lets you avoid adding yet another large dependency outside of Python&amp;rsquo;s standard library.&lt;/p&gt;
&lt;p&gt;But by all means, check out &lt;code&gt;curio&lt;/code&gt; and &lt;code&gt;trio&lt;/code&gt;, and you might find that they get the same thing done in a way that&amp;rsquo;s more intuitive for you as the user.  Many of the package-agnostic concepts presented here should permeate to alternative async IO packages as well.&lt;/p&gt;
&lt;h2 id=&quot;odds-and-ends&quot;&gt;Odds and Ends&lt;/h2&gt;
&lt;p&gt;In these next few sections, you&amp;rsquo;ll cover some miscellaneous parts of &lt;code&gt;asyncio&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; that haven&amp;rsquo;t fit neatly into the tutorial thus far, but are still important for building and understanding a full program.&lt;/p&gt;
&lt;h3 id=&quot;other-top-level-asyncio-functions&quot;&gt;Other Top-Level &lt;code&gt;asyncio&lt;/code&gt; Functions&lt;/h3&gt;
&lt;p&gt;In addition to &lt;code&gt;asyncio.run()&lt;/code&gt;, you&amp;rsquo;ve seen a few other package-level functions such as &lt;code&gt;asyncio.create_task()&lt;/code&gt; and &lt;code&gt;asyncio.gather()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can use &lt;code&gt;create_task()&lt;/code&gt; to schedule the execution of a coroutine object, followed by &lt;code&gt;asyncio.run()&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;asyncio&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;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;coro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seq&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;nb&quot;&gt;list&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&amp;#39;IO&amp;#39; wait time is proportional to the max element.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&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;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seq&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;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;reversed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seq&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;k&quot;&gt;async&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# This is a bit redundant in the case of one task&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# We could use `await coro([3, 2, 1])` on its own&lt;/span&gt;
&lt;span class=&quot;gp&quot;&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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coro&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;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;c1&quot;&gt;# Python 3.7+&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&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;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;t: type {type(t)}&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;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;t done: {t.done()}&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&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;span class=&quot;go&quot;&gt;t: type &amp;lt;class &amp;#39;_asyncio.Task&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;t done: True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There&amp;rsquo;s a subtlety to this pattern: if you don&amp;rsquo;t &lt;code&gt;await t&lt;/code&gt; within &lt;code&gt;main()&lt;/code&gt;, it may finish before &lt;code&gt;main()&lt;/code&gt; itself signals that it is complete.  Because &lt;code&gt;asyncio.run(main())&lt;/code&gt; &lt;a href=&quot;https://github.com/python/cpython/blob/7e18deef652a9d413d5dbd19d61073ba7eb5460e/Lib/asyncio/runners.py#L43&quot;&gt;calls &lt;code&gt;loop.run_until_complete(main())&lt;/code&gt;&lt;/a&gt;, the event loop is only concerned (without &lt;code&gt;await t&lt;/code&gt; present) that &lt;code&gt;main()&lt;/code&gt; is done, not that the tasks that get created within &lt;code&gt;main()&lt;/code&gt; are done.  Without &lt;code&gt;await t&lt;/code&gt;, the loop&amp;rsquo;s other tasks &lt;a href=&quot;https://github.com/python/cpython/blob/7e18deef652a9d413d5dbd19d61073ba7eb5460e/Lib/asyncio/runners.py#L46&quot;&gt;will be cancelled&lt;/a&gt;, possibly before they are completed.  If you need to get a list of currently pending tasks, you can use &lt;code&gt;asyncio.Task.all_tasks()&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;: &lt;code&gt;asyncio.create_task()&lt;/code&gt; was introduced in Python 3.7.  In Python 3.6 or lower, use &lt;code&gt;asyncio.ensure_future()&lt;/code&gt; in place of &lt;code&gt;create_task()&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Separately, there&amp;rsquo;s &lt;code&gt;asyncio.gather()&lt;/code&gt;.  While it doesn&amp;rsquo;t do anything tremendously special, &lt;code&gt;gather()&lt;/code&gt; is meant to neatly put a collection of coroutines (futures) into a single future.  As a result, it returns a single future object, and, if you &lt;code&gt;await asyncio.gather()&lt;/code&gt; and specify multiple tasks or coroutines, you&amp;rsquo;re waiting for all of them to be completed.  (This somewhat parallels &lt;code&gt;queue.join()&lt;/code&gt; from our earlier example.)  The result of &lt;code&gt;gather()&lt;/code&gt; will be a list of the results across the inputs:&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;time&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;async&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;gp&quot;&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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coro&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;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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;t2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coro&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;mi&quot;&gt;5&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;c1&quot;&gt;# Python 3.7+&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;s1&quot;&gt;&amp;#39;Start:&amp;#39;&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;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%X&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;gp&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t2&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;s1&quot;&gt;&amp;#39;End:&amp;#39;&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;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%X&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;c1&quot;&gt;# Should be 10 seconds&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;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Both tasks done: {all((t.done(), t2.done()))}&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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&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;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&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;span class=&quot;go&quot;&gt;Start: 16:20:11&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;End: 16:20:21&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Both tasks done: True&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;a&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[[1, 2, 3], [0, 5, 10]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You probably noticed that &lt;code&gt;gather()&lt;/code&gt; waits on the entire result set of the Futures or coroutines that you pass it.  Alternatively, you can loop over &lt;code&gt;asyncio.as_completed()&lt;/code&gt; to get tasks as they are completed, in the order of completion.  The function returns an iterator that yields tasks as they finish.  Below, the result of &lt;code&gt;coro([3, 2, 1])&lt;/code&gt; will be available before &lt;code&gt;coro([10, 5, 0])&lt;/code&gt; is complete, which is not the case with &lt;code&gt;gather()&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;k&quot;&gt;async&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;gp&quot;&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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coro&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;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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;t2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coro&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;mi&quot;&gt;5&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;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;s1&quot;&gt;&amp;#39;Start:&amp;#39;&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;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%X&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;as_completed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t2&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;compl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&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;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;res: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{compl}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; completed at {time.strftime(&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%X&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;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;s1&quot;&gt;&amp;#39;End:&amp;#39;&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;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%X&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;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;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Both tasks done: {all((t.done(), t2.done()))}&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;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;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&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;span class=&quot;go&quot;&gt;Start: 09:49:07&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;res: [1, 2, 3] completed at 09:49:10&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;res: [0, 5, 10] completed at 09:49:17&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;End: 09:49:17&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Both tasks done: True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Lastly, you may also see &lt;code&gt;asyncio.ensure_future()&lt;/code&gt;.  You should rarely need it, because it&amp;rsquo;s a lower-level plumbing API and largely replaced by &lt;code&gt;create_task()&lt;/code&gt;, which was introduced later.&lt;/p&gt;
&lt;h3 id=&quot;the-precedence-of-await&quot;&gt;The Precedence of &lt;code&gt;await&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;While they behave somewhat similarly, the &lt;code&gt;await&lt;/code&gt; keyword has significantly higher precedence than &lt;code&gt;yield&lt;/code&gt;.  This means that, because it is more tightly bound, there are a number of instances where you&amp;rsquo;d need parentheses in a &lt;code&gt;yield from&lt;/code&gt; statement that are not required in an analogous &lt;code&gt;await&lt;/code&gt; statement.  For more information, see &lt;a href=&quot;https://www.python.org/dev/peps/pep-0492/#examples-of-await-expressions&quot;&gt;examples of &lt;code&gt;await&lt;/code&gt; expressions&lt;/a&gt; from PEP 492.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;re now equipped to use &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; and the libraries built off of it.  Here&amp;rsquo;s a recap of what you&amp;rsquo;ve covered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Asynchronous IO as a language-agnostic model and a way to effect concurrency by letting coroutines indirectly communicate with each other&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The specifics of Python&amp;rsquo;s new &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; keywords, used to mark and define coroutines&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;asyncio&lt;/code&gt;, the Python package that provides the API to run and manage coroutines&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;
&lt;h3 id=&quot;python-version-specifics&quot;&gt;Python Version Specifics&lt;/h3&gt;
&lt;p&gt;Async IO in Python has evolved swiftly, and it can be hard to keep track of what came when.  Here&amp;rsquo;s a list of Python minor-version changes and introductions related to &lt;code&gt;asyncio&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;3.3: The &lt;code&gt;yield from&lt;/code&gt; expression allows for generator delegation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.4: &lt;code&gt;asyncio&lt;/code&gt; was introduced in the Python standard library with provisional API status.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.5: &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; became a part of the Python grammar, used to signify and wait on coroutines.  They were not yet reserved keywords.  (You could still define functions or variables named &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.6: Asynchronous generators and asynchronous comprehensions were introduced.  The API of &lt;code&gt;asyncio&lt;/code&gt; was declared stable rather than provisional.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.7: &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; became reserved keywords.  (They cannot be used as identifiers.)  They are intended to replace the &lt;code&gt;asyncio.coroutine()&lt;/code&gt; decorator.  &lt;code&gt;asyncio.run()&lt;/code&gt; was introduced to the &lt;code&gt;asyncio&lt;/code&gt; package, among &lt;a href=&quot;https://docs.python.org/3/whatsnew/3.7.html#whatsnew37-asyncio&quot;&gt;a bunch of other features&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to be safe (and be able to use &lt;code&gt;asyncio.run()&lt;/code&gt;), go with Python 3.7 or above to get the full set of features.&lt;/p&gt;
&lt;h3 id=&quot;articles&quot;&gt;Articles&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s a curated list of additional resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Real Python: &lt;a href=&quot;https://realpython.com/python-concurrency/&quot;&gt;Speed up your Python Program with Concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Real Python: &lt;a href=&quot;https://realpython.com/python-gil/&quot;&gt;What is the Python Global Interpreter Lock?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CPython: The &lt;code&gt;asyncio&lt;/code&gt; package &lt;a href=&quot;https://github.com/python/cpython/tree/master/Lib/asyncio&quot;&gt;source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Python docs: &lt;a href=&quot;https://docs.python.org/3/reference/datamodel.html#coroutines&quot;&gt;Data model &amp;gt; Coroutines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;TalkPython: &lt;a href=&quot;https://training.talkpython.fm/courses/details/async-in-python-with-threading-and-multiprocessing&quot;&gt;Async Techniques and Examples in Python&lt;/a&gt; &lt;a href=&quot;https://github.com/talkpython/async-techniques-python-course&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Brett Cannon: &lt;a href=&quot;https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/&quot;&gt;How the Heck Does Async-Await Work in Python 3.5?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PYMOTW: &lt;a href=&quot;https://pymotw.com/3/asyncio/&quot;&gt;&lt;code&gt;asyncio&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A. Jesse Jiryu Davis and Guido van Rossum: &lt;a href=&quot;http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html&quot;&gt;A Web Crawler With asyncio Coroutines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Andy Pearce: &lt;a href=&quot;http://www.andy-pearce.com/blog/posts/2016/Jun/the-state-of-python-coroutines-yield-from/&quot;&gt;The State of Python Coroutines: &lt;code&gt;yield from&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Nathaniel J. Smith: &lt;a href=&quot;https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/&quot;&gt;Some Thoughts on Asynchronous API Design in a Post-&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; World&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Armin Ronacher: &lt;a href=&quot;http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/&quot;&gt;I don&amp;rsquo;t understand Python&amp;rsquo;s Asyncio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Andy Balaam: &lt;a href=&quot;http://www.artificialworlds.net/blog/2017/05/31/basic-ideas-of-python-3-asyncio-concurrency/&quot;&gt;series on &lt;code&gt;asyncio&lt;/code&gt;&lt;/a&gt; (4 posts)&lt;/li&gt;
&lt;li&gt;Stack Overflow: &lt;a href=&quot;https://stackoverflow.com/q/40836800/7954504&quot;&gt;Python &lt;code&gt;asyncio.semaphore&lt;/code&gt; in &lt;code&gt;async&lt;/code&gt;-&lt;code&gt;await&lt;/code&gt; function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Yeray Diaz:&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hackernoon.com/asyncio-for-the-working-python-developer-5c468e6e2e8e&quot;&gt;AsyncIO for the Working Python Developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/python-pandemonium/asyncio-coroutine-patterns-beyond-await-a6121486656f&quot;&gt;Asyncio Coroutine Patterns: Beyond &lt;code&gt;await&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few Python &lt;em&gt;What&amp;rsquo;s New&lt;/em&gt; sections explain the motivation behind language changes in more detail:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/whatsnew/3.3.html#pep-380&quot;&gt;What&amp;rsquo;s New in Python 3.3&lt;/a&gt; (&lt;code&gt;yield from&lt;/code&gt; and PEP 380)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep525&quot;&gt;What&amp;rsquo;s New in Python 3.6&lt;/a&gt; (PEP 525 &amp;amp; 530)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From David Beazley:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dabeaz.com/generators/&quot;&gt;Generator: Tricks for Systems Programmers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dabeaz.com/coroutines/&quot;&gt;A Curious Course on Coroutines and Concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://dabeaz.com/finalgenerator/index.html&quot;&gt;Generators: The Final Frontier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;YouTube talks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/0kXaLh8Fz3k&quot;&gt;John Reese - Thinking Outside the GIL with AsyncIO and Multiprocessing - PyCon 2018&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/ZzfHjytDceU&quot;&gt;Keynote David Beazley - Topics of Interest (Python Asyncio)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/MCs5OvhV9S4&quot;&gt;David Beazley - Python Concurrency From the Ground Up: LIVE! - PyCon 2015&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/9zinZmE3Ogk&quot;&gt;Raymond Hettinger, Keynote on Concurrency, PyBay 2017&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/Bv25Dwe84g0&quot;&gt;Thinking about Concurrency, Raymond Hettinger, Python core developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/iG6fr81xHKA&quot;&gt;Miguel Grinberg Asynchronous Python for the Complete Beginner PyCon 2017&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/2ZFFv-wZ8_g&quot;&gt;Yury Selivanov asyncawait and asyncio in Python 3 6 and beyond PyCon 2017&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/E-1Y4kSsAFc&quot;&gt;Fear and Awaiting in Async: A Savage Journey to the Heart of the Coroutine Dream&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/kdzL3r-yJZY&quot;&gt;What Is Async, How Does It Work, and When Should I Use It? (PyCon APAC 2014)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;related-peps&quot;&gt;Related PEPs&lt;/h3&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;PEP&lt;/th&gt;
&lt;th&gt;Date Created&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0342/&quot;&gt;PEP 342 &amp;ndash; Coroutines via Enhanced Generators&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2005-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0380/&quot;&gt;PEP 380 &amp;ndash; Syntax for Delegating to a Subgenerator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2009-02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-3153/&quot;&gt;PEP 3153 &amp;ndash; Asynchronous IO support&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2011-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-3156/&quot;&gt;PEP 3156 &amp;ndash; Asynchronous IO Support Rebooted: the &amp;ldquo;asyncio&amp;rdquo; Module&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2012-12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0492/&quot;&gt;PEP 492 &amp;ndash; Coroutines with async and await syntax&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2015-04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0525/&quot;&gt;PEP 525 &amp;ndash; Asynchronous Generators&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2016-07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0530/&quot;&gt;PEP 530 &amp;ndash; Asynchronous Comprehensions&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2016-09&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;libraries-that-work-with-asyncawait&quot;&gt;Libraries That Work With &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;From &lt;a href=&quot;https://github.com/aio-libs&quot;&gt;aio-libs&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiohttp&quot;&gt;&lt;code&gt;aiohttp&lt;/code&gt;&lt;/a&gt;: Asynchronous HTTP client/server framework&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aioredis&quot;&gt;&lt;code&gt;aioredis&lt;/code&gt;&lt;/a&gt;: Async IO Redis support&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiopg&quot;&gt;&lt;code&gt;aiopg&lt;/code&gt;&lt;/a&gt;: Async IO PostgreSQL support&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiomcache&quot;&gt;&lt;code&gt;aiomcache&lt;/code&gt;&lt;/a&gt;: Async IO memcached client&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiokafka&quot;&gt;&lt;code&gt;aiokafka&lt;/code&gt;&lt;/a&gt;: Async IO Kafka client&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiozmq&quot;&gt;&lt;code&gt;aiozmq&lt;/code&gt;&lt;/a&gt;: Async IO ZeroMQ support&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiojobs&quot;&gt;&lt;code&gt;aiojobs&lt;/code&gt;&lt;/a&gt;: Jobs scheduler for managing background tasks&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/async_lru&quot;&gt;&lt;code&gt;async_lru&lt;/code&gt;&lt;/a&gt;: Simple LRU cache for async IO&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From &lt;a href=&quot;https://magic.io/&quot;&gt;magicstack&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/MagicStack/uvloop&quot;&gt;&lt;code&gt;uvloop&lt;/code&gt;&lt;/a&gt;: Ultra fast async IO event loop&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/MagicStack/asyncpg&quot;&gt;&lt;code&gt;asyncpg&lt;/code&gt;&lt;/a&gt;: (Also very fast) async IO PostgreSQL support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From other hosts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/python-trio/trio&quot;&gt;&lt;code&gt;trio&lt;/code&gt;&lt;/a&gt;: Friendlier &lt;code&gt;asyncio&lt;/code&gt; intended to showcase a radically simpler design&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Tinche/aiofiles&quot;&gt;&lt;code&gt;aiofiles&lt;/code&gt;&lt;/a&gt;: Async file IO&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/theelous3/asks&quot;&gt;&lt;code&gt;asks&lt;/code&gt;&lt;/a&gt;: Async requests-like http library&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jonathanslenders/asyncio-redis&quot;&gt;&lt;code&gt;asyncio-redis&lt;/code&gt;&lt;/a&gt;: Async IO Redis support&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dano/aioprocessing&quot;&gt;&lt;code&gt;aioprocessing&lt;/code&gt;&lt;/a&gt;: Integrates &lt;code&gt;multiprocessing&lt;/code&gt; module with &lt;code&gt;asyncio&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Scille/umongo&quot;&gt;&lt;code&gt;umongo&lt;/code&gt;&lt;/a&gt;: Async IO MongoDB client&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/alex-sherman/unsync&quot;&gt;&lt;code&gt;unsync&lt;/code&gt;&lt;/a&gt;: Unsynchronize &lt;code&gt;asyncio&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vxgmichel/aiostream&quot;&gt;&lt;code&gt;aiostream&lt;/code&gt;&lt;/a&gt;: Like &lt;code&gt;itertools&lt;/code&gt;, but async&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>Speed Up Your Python Program With Concurrency</title>
      <id>https://realpython.com/python-concurrency/</id>
      <link href="https://realpython.com/python-concurrency/"/>
      <updated>2019-01-14T14:00:00+00:00</updated>
      <summary>Learn what concurrency means in Python and why you might want to use it. You&#39;ll see a simple, non-concurrent approach and then look into why you&#39;d want threading, asyncio, or multiprocessing.</summary>
      <content type="html">
        &lt;p&gt;If you&amp;rsquo;ve heard lots of talk about &lt;code&gt;asyncio&lt;/code&gt; &lt;a href=&quot;https://realpython.com/python37-new-features/&quot;&gt;being added to Python&lt;/a&gt; but are curious how it compares to other concurrency methods or are wondering what concurrency is and how it might speed up your program, you&amp;rsquo;ve come to the right place.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn the following:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What &lt;strong&gt;concurrency&lt;/strong&gt; is&lt;/li&gt;
&lt;li&gt;What &lt;strong&gt;parallelism&lt;/strong&gt; is&lt;/li&gt;
&lt;li&gt;How some of &lt;strong&gt;Python&amp;rsquo;s concurrency methods&lt;/strong&gt; compare, including &lt;code&gt;threading&lt;/code&gt;, &lt;code&gt;asyncio&lt;/code&gt;, and &lt;code&gt;multiprocessing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When to use concurrency&lt;/strong&gt; in your program and which module to use&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article assumes that you have a basic understanding of Python and that you&amp;rsquo;re using at least version 3.6 to run the examples. You can download the examples from the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/concurrency-overview&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;h2 id=&quot;what-is-concurrency&quot;&gt;What Is Concurrency?&lt;/h2&gt;
&lt;p&gt;The dictionary definition of concurrency is simultaneous occurrence. In Python, the things that are occurring simultaneously are called by different names (thread, task, process) but at a high level, they all refer to a sequence of instructions that run in order.&lt;/p&gt;
&lt;p&gt;I like to think of them as different trains of thought. Each one can be stopped at certain points, and the CPU or brain that is processing them can switch to a different one. The state of each one is saved so it can be restarted right where it was interrupted.&lt;/p&gt;
&lt;p&gt;You might wonder why Python uses different words for the same concept. It turns out that threads, tasks, and processes are only the same if you view them from a high level. Once you start digging into the details, they all represent slightly different things. You&amp;rsquo;ll see more of how they are different as you progress through the examples.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s talk about the simultaneous part of that definition. You have to be a little careful because, when you get down to the details, only &lt;code&gt;multiprocessing&lt;/code&gt; actually runs these trains of thought at literally the same time. &lt;code&gt;Threading&lt;/code&gt; and &lt;code&gt;asyncio&lt;/code&gt; both run on a single processor and therefore only run one at a time. They just cleverly find ways to take turns to speed up the overall process. Even though they don&amp;rsquo;t run different trains of thought simultaneously, we still call this concurrency.&lt;/p&gt;
&lt;p&gt;The way the threads or tasks take turns is the big difference between &lt;code&gt;threading&lt;/code&gt; and &lt;code&gt;asyncio&lt;/code&gt;. In &lt;code&gt;threading&lt;/code&gt;, the operating system actually knows about each thread and can interrupt it at any time to start running a different thread. This is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Preemption_%28computing%29#Preemptive_multitasking&quot;&gt;pre-emptive multitasking&lt;/a&gt; since the operating system can pre-empt your thread to make the switch.&lt;/p&gt;
&lt;p&gt;Pre-emptive multitasking is handy in that the code in the thread doesn&amp;rsquo;t need to do anything to make the switch. It can also be difficult because of that &amp;ldquo;at any time&amp;rdquo; phrase. This switch can happen in the middle of a single Python statement, even a trivial one like &lt;code&gt;x = x + 1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Asyncio&lt;/code&gt;, on the other hand, uses &lt;a href=&quot;https://en.wikipedia.org/wiki/Cooperative_multitasking&quot;&gt;cooperative multitasking&lt;/a&gt;. The tasks must cooperate by announcing when they are ready to be switched out. That means that the code in the task has to change slightly to make this happen.&lt;/p&gt;
&lt;p&gt;The benefit of doing this extra work up front is that you always know where your task will be swapped out. It will not be swapped out in the middle of a Python statement unless that statement is marked. You&amp;rsquo;ll see later how this can simplify parts of your design.&lt;/p&gt;
&lt;h2 id=&quot;what-is-parallelism&quot;&gt;What Is Parallelism?&lt;/h2&gt;
&lt;p&gt;So far, you&amp;rsquo;ve looked at concurrency that happens on a single processor. What about all of those CPU cores your cool, new laptop has? How can you make use of them? &lt;code&gt;multiprocessing&lt;/code&gt; is the answer.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;multiprocessing&lt;/code&gt;, Python creates new processes. A process here can be thought of as almost a completely different program, though technically they&amp;rsquo;re usually defined as a collection of resources where the resources include memory, file handles and things like that. One way to think about it is that each process runs in its own Python interpreter.&lt;/p&gt;
&lt;p&gt;Because they are different processes, each of your trains of thought in a multiprocessing program can run on a different core. Running on a different core means that they actually can run at the same time, which is fabulous. There are some complications that arise from doing this, but Python does a pretty good job of smoothing them over most of the time.&lt;/p&gt;
&lt;p&gt;Now that you have an idea of what concurrency and parallelism are, let&amp;rsquo;s review their differences, and then we can look at why they can be useful:&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;Concurrency Type&lt;/th&gt;
&lt;th&gt;Switching Decision&lt;/th&gt;
&lt;th&gt;Number of Processors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pre-emptive multitasking (&lt;code&gt;threading&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The operating system decides when to switch tasks external to Python.&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cooperative multitasking (&lt;code&gt;asyncio&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The tasks decide when to give up control.&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiprocessing (&lt;code&gt;multiprocessing&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The processes all run at the same time on different processors.&lt;/td&gt;
&lt;td&gt;Many&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Each of these types of concurrency can be useful. Let&amp;rsquo;s take a look at what types of programs they can help you speed up.&lt;/p&gt;
&lt;h2 id=&quot;when-is-concurrency-useful&quot;&gt;When Is Concurrency Useful?&lt;/h2&gt;
&lt;p&gt;Concurrency can make a big difference for two types of problems. These are generally called CPU-bound and I/O-bound.&lt;/p&gt;
&lt;p&gt;I/O-bound problems cause your program to slow down because it frequently must wait for input/output (I/O) from some external resource. They arise frequently when your program is working with things that are much slower than your CPU.&lt;/p&gt;
&lt;p&gt;Examples of things that are slower than your CPU are legion, but your program thankfully does not interact with most of them. The slow things your program will interact with most frequently are the file system and network connections.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see what that looks like:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/IOBound.4810a888b457.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-100&quot; src=&quot;https://files.realpython.com/media/IOBound.4810a888b457.png&quot; width=&quot;1737&quot; height=&quot;537&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/IOBound.4810a888b457.png&amp;amp;w=434&amp;amp;sig=9a0794c8f7e8424f5caf6d1bcbd555fcdd612b68 434w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/IOBound.4810a888b457.png&amp;amp;w=868&amp;amp;sig=f1a9d0fbb4e7f743a55ee0fec76eb627cfbf3571 868w, https://files.realpython.com/media/IOBound.4810a888b457.png 1737w&quot; sizes=&quot;75vw&quot; alt=&quot;Timing Diagram of an I/O Bound Program&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the diagram above, the blue boxes show time when your program is doing work, and the red boxes are time spent waiting for an I/O operation to complete. This diagram is not to scale because requests on the internet can take several orders of magnitude longer than CPU instructions, so your program can end up spending most of its time waiting. This is what your browser is doing most of the time.&lt;/p&gt;
&lt;p&gt;On the flip side, there are classes of programs that do significant computation without talking to the network or accessing a file. These are the CPU-bound programs, because the resource limiting the speed of your program is the CPU, not the network or the file system.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a corresponding diagram for a CPU-bound program:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/CPUBound.d2d32cb2626c.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-100&quot; src=&quot;https://files.realpython.com/media/CPUBound.d2d32cb2626c.png&quot; width=&quot;1737&quot; height=&quot;486&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CPUBound.d2d32cb2626c.png&amp;amp;w=434&amp;amp;sig=6bbdd46096cca225291e357d86691e08939744a4 434w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CPUBound.d2d32cb2626c.png&amp;amp;w=868&amp;amp;sig=88b3c863f684b8109ed17e4a013aab1e666dba1f 868w, https://files.realpython.com/media/CPUBound.d2d32cb2626c.png 1737w&quot; sizes=&quot;75vw&quot; alt=&quot;Timing Diagram of an CPU Bound Program&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you work through the examples in the following section, you&amp;rsquo;ll see that different forms of concurrency work better or worse with CPU-bound and I/O-bound programs. Adding concurrency to your program adds extra code and complications, so you&amp;rsquo;ll need to decide if the potential speed up is worth the extra effort. By the end of this article, you should have enough info to start making that decision.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a quick summary to clarify this concept:&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;I/O-Bound Process&lt;/th&gt;
&lt;th&gt;CPU-Bound Process&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Your program spends most of its time talking to a slow device, like a network connection, a hard drive, or a printer.&lt;/td&gt;
&lt;td&gt;You program spends most of its time doing CPU operations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speeding it up involves overlapping the times spent waiting for these devices.&lt;/td&gt;
&lt;td&gt;Speeding it up involves finding ways to do more computations in the same amount of time.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;You&amp;rsquo;ll look at I/O-bound programs first. Then, you&amp;rsquo;ll get to see some code dealing with CPU-bound programs.&lt;/p&gt;
&lt;h2 id=&quot;how-to-speed-up-an-io-bound-program&quot;&gt;How to Speed Up an I/O-Bound Program&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start by focusing on I/O-bound programs and a common problem: downloading content over the network. For our example, you will be downloading web pages from a few sites, but it really could be any network traffic. It&amp;rsquo;s just easier to visualize and set up with web pages.&lt;/p&gt;
&lt;h3 id=&quot;synchronous-version&quot;&gt;Synchronous Version&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;ll start with a non-concurrent version of this task. Note that this program requires the &lt;a href=&quot;http://docs.python-requests.org/en/master/&quot;&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; module. You should run &lt;code&gt;pip install requests&lt;/code&gt; before running it, probably using a &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;virtualenv&lt;/a&gt;. This version does not use concurrency at all:&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;time&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_site&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;n&quot;&gt;session&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;session&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;url&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;response&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;s2&quot;&gt;&amp;quot;Read {len(response.content)} from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{url}&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&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;session&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;url&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;download_site&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;n&quot;&gt;session&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;n&quot;&gt;sites&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;http://www.jython.org&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://olympus.realpython.org/dice&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;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&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;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&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;time&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;start_time&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;Downloaded {len(sites)} in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&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, this is a fairly short program. &lt;code&gt;download_site()&lt;/code&gt; just downloads the contents from a URL and prints the size. One small thing to point out is that we&amp;rsquo;re using a &lt;a href=&quot;http://docs.python-requests.org/en/master/user/advanced/#session-objects&quot;&gt;&lt;code&gt;Session&lt;/code&gt;&lt;/a&gt; object from &lt;code&gt;requests&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is possible to simply use &lt;code&gt;get()&lt;/code&gt; from &lt;code&gt;requests&lt;/code&gt; directly, but creating a &lt;code&gt;Session&lt;/code&gt; object allows &lt;code&gt;requests&lt;/code&gt; to do some fancy networking tricks and really speed things up.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;download_all_sites()&lt;/code&gt; creates the &lt;code&gt;Session&lt;/code&gt; and then walks through the list of sites, downloading each one in turn. Finally, it prints out how long this process took so you can have the satisfaction of seeing how much concurrency has helped us in the following examples.&lt;/p&gt;
&lt;p&gt;The processing diagram for this program will look much like the I/O-bound diagram in the last section.&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; Network traffic is dependent on many factors that can vary from second to second. I&amp;rsquo;ve seen the times of these tests double from one run to another due to network issues.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Why the Synchronous Version Rocks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The great thing about this version of code is that, well, it&amp;rsquo;s easy. It was comparatively easy to write and debug. It&amp;rsquo;s also more straight-forward to think about. There&amp;rsquo;s only one train of thought running through it, so you can predict what the next step is and how it will behave.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Problems With the Synchronous Version&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The big problem here is that it&amp;rsquo;s relatively slow compared to the other solutions we&amp;rsquo;ll provide. Here&amp;rsquo;s an example of what the final output gave on 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; ./io_non_concurrent.py
&lt;span class=&quot;go&quot;&gt;   [most output skipped]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Downloaded 160 in 14.289619207382202 seconds&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; Your results may vary significantly. When running this script, I saw the times vary from 14.2 to 21.9 seconds. For this article, I took the fastest of three runs as the time. The differences between the methods will still be clear.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Being slower isn&amp;rsquo;t always a big issue, however. If the program you&amp;rsquo;re running takes only 2 seconds with a synchronous version and is only run rarely, it&amp;rsquo;s probably not worth adding concurrency. You can stop here.&lt;/p&gt;
&lt;p&gt;What if your program is run frequently? What if it takes hours to run? Let&amp;rsquo;s move on to concurrency by rewriting this program using &lt;code&gt;threading&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;threading-version&quot;&gt;&lt;code&gt;threading&lt;/code&gt; Version&lt;/h3&gt;
&lt;p&gt;As you probably guessed, writing a threaded program takes more effort. You might be surprised at how little extra effort it takes for simple cases, however. Here&amp;rsquo;s what the same program looks like with &lt;code&gt;threading&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;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;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;n&quot;&gt;thread_local&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;local&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_session&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;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_local&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;session&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&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;thread_local&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&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;Session&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;thread_local&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_site&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;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_session&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;session&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;url&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;response&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;s2&quot;&gt;&amp;quot;Read {len(response.content)} from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{url}&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&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;5&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;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sites&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;n&quot;&gt;sites&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;http://www.jython.org&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://olympus.realpython.org/dice&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;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&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;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&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;time&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;start_time&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;Downloaded {len(sites)} in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&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 you add &lt;code&gt;threading&lt;/code&gt;, the overall structure is the same and you only needed to make a few changes. &lt;code&gt;download_all_sites()&lt;/code&gt; changed from calling the function once per site to a more complex structure.&lt;/p&gt;
&lt;p&gt;In this version, you&amp;rsquo;re creating a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;, which seems like a complicated thing. Let&amp;rsquo;s break that down: &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; = &lt;code&gt;Thread&lt;/code&gt; + &lt;code&gt;Pool&lt;/code&gt; + &lt;code&gt;Executor&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You already know about the &lt;code&gt;Thread&lt;/code&gt; part. That&amp;rsquo;s just a train of thought we mentioned earlier. The &lt;code&gt;Pool&lt;/code&gt; portion is where it starts to get interesting. This object is going to create a pool of threads, each of which can run concurrently. Finally, the &lt;code&gt;Executor&lt;/code&gt; is the part that&amp;rsquo;s going to control how and when each of the threads in the pool will run. It will execute the request in the pool.&lt;/p&gt;
&lt;p&gt;Helpfully, the standard library implements &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; as a context manager so you can use the &lt;code&gt;with&lt;/code&gt; syntax to manage creating and freeing the pool of &lt;code&gt;Threads&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once you have a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;, you can use its handy &lt;code&gt;.map()&lt;/code&gt; method. This method runs the passed-in function on each of the sites in the list. The great part is that it automatically runs them concurrently using the pool of threads it is managing.&lt;/p&gt;
&lt;p&gt;Those of you coming from other languages, or even Python 2, are probably wondering where the usual objects and functions are that manage the details you&amp;rsquo;re used to when dealing with &lt;code&gt;threading&lt;/code&gt;, things like &lt;code&gt;Thread.start()&lt;/code&gt;, &lt;code&gt;Thread.join()&lt;/code&gt;, and &lt;code&gt;Queue&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These are all still there, and you can use them to achieve fine-grained control of how your threads are run. But, starting with Python 3.2, the standard library added a higher-level abstraction called &lt;code&gt;Executors&lt;/code&gt; that  manage many of the details for you if you don&amp;rsquo;t need that fine-grained control.&lt;/p&gt;
&lt;p&gt;The other interesting change in our example is that each thread needs to create its own &lt;code&gt;requests.Session()&lt;/code&gt; object. When you&amp;rsquo;re looking at the documentation for &lt;code&gt;requests&lt;/code&gt;, it&amp;rsquo;s not necessarily easy to tell, but reading &lt;a href=&quot;https://github.com/requests/requests/issues/2766&quot;&gt;this issue&lt;/a&gt;, it seems fairly clear that you need a separate Session for each thread.&lt;/p&gt;
&lt;p&gt;This is one of the interesting and difficult issues with &lt;code&gt;threading&lt;/code&gt;. Because the operating system is in control of when your task gets interrupted and another task starts, any data that is shared between the threads needs to be protected, or thread-safe. Unfortunately &lt;code&gt;requests.Session()&lt;/code&gt; is not thread-safe.&lt;/p&gt;
&lt;p&gt;There are several strategies for making data accesses thread-safe depending on what the data is and how you&amp;rsquo;re using it. One of them is to use thread-safe data structures like &lt;code&gt;Queue&lt;/code&gt; from Python&amp;rsquo;s &lt;code&gt;queue&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;These objects use low-level primitives like &lt;a href=&quot;https://docs.python.org/2/library/threading.html#lock-objects&quot;&gt;&lt;code&gt;threading.Lock&lt;/code&gt;&lt;/a&gt; to ensure that only one thread can access a block of code or a bit of memory at the same time. You are using this strategy indirectly by way of the &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;Another strategy to use here is something called thread local storage. &lt;code&gt;Threading.local()&lt;/code&gt; creates an object that look like a global but is specific to each individual thread. In your example, this is done with &lt;code&gt;threadLocal&lt;/code&gt; and &lt;code&gt;get_session()&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;threadLocal&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;local&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_session&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;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threadLocal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;session&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&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;ow&quot;&gt;is&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;threadLocal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&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;Session&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;threadLocal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;ThreadLocal&lt;/code&gt; is in the &lt;code&gt;threading&lt;/code&gt; module to specifically solve this problem. It looks a little odd, but you only want to create one of these objects, not one for each thread. The object itself takes care of separating accesses from different threads to different data.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;get_session()&lt;/code&gt; is called, the &lt;code&gt;session&lt;/code&gt; it looks up is specific to the particular thread on which it&amp;rsquo;s running. So each thread will create a single session the first time it calls &lt;code&gt;get_session()&lt;/code&gt; and then will simply use that session on each subsequent call throughout its lifetime.&lt;/p&gt;
&lt;p&gt;Finally, a quick note about picking the number of threads. You can see that the example code uses 5 threads. Feel free to play around with this number and see how the overall time changes. You might expect that having one thread per download would be the fastest but, at least on my system it was not. I found the fastest results somewhere between 5 and 10 threads. If you go any higher than that, then the extra overhead of creating and destroying the threads erases any time savings.&lt;/p&gt;
&lt;p&gt;The difficult answer here is that the correct number of threads is not a constant from one task to another. Some experimentation is required.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why the &lt;code&gt;threading&lt;/code&gt; Version Rocks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s fast! Here&amp;rsquo;s the fastest run of my tests. Remember that the non-concurrent version took more than 14 seconds:&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; ./io_threading.py
&lt;span class=&quot;go&quot;&gt;   [most output skipped]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Downloaded 160 in 3.7238826751708984 seconds&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s what its execution timing diagram looks like:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Threading.3eef48da829e.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/Threading.3eef48da829e.png&quot; width=&quot;1197&quot; height=&quot;537&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Threading.3eef48da829e.png&amp;amp;w=299&amp;amp;sig=3fa41f9fd4fadcde180155707620dcd593c6a56f 299w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Threading.3eef48da829e.png&amp;amp;w=598&amp;amp;sig=fbfdfa285a8e3f0872dc5d61de021c385f6e0ec1 598w, https://files.realpython.com/media/Threading.3eef48da829e.png 1197w&quot; sizes=&quot;75vw&quot; alt=&quot;Timing Diagram of a Threading Solution&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It uses multiple threads to have multiple open requests out to web sites at the same time, allowing your program to overlap the waiting times and get the final result faster! Yippee! That was the goal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Problems with the &lt;code&gt;threading&lt;/code&gt; Version&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Well, as you can see from the example, it takes a little more code to make this happen, and you really have to give some thought to what data is shared between threads.&lt;/p&gt;
&lt;p&gt;Threads can interact in ways that are subtle and hard to detect. These interactions can cause race conditions that frequently result in random, intermittent bugs that can be quite difficult to find. Those of you who are unfamiliar with the concept of race conditions might want to expand and read the section below.&lt;/p&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_card7c5753&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;#collapse7c5753&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse7c5753&quot;&gt;Race Conditions&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapse7c5753&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse7c5753&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapse7c5753&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_card7c5753&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;Race conditions are an entire class of subtle bugs that can and frequently do happen in multi-threaded code. Race conditions happen because the programmer has not sufficiently protected data accesses to prevent threads from interfering with each other. You need to take extra steps when writing threaded code to ensure things are thread-safe.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s going on here is that the operating system is controlling when your thread runs and when it gets swapped out to let another thread run. This thread swapping can occur at any point, even while doing sub-steps of a Python statement. As a quick example, look at this 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;concurrent.futures&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;counter&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;increment_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fake_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&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;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;counter&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;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;n&quot;&gt;fake_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;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;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;counter&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;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;5000&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;increment_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fake_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code is quite similar to the structure you used in the &lt;code&gt;threading&lt;/code&gt; example above. The difference is that each of the threads is accessing the same global variable &lt;code&gt;counter&lt;/code&gt; and incrementing it. &lt;code&gt;Counter&lt;/code&gt; is not protected in any way, so it is not thread-safe.&lt;/p&gt;
&lt;p&gt;In order to increment &lt;code&gt;counter&lt;/code&gt;, each of the threads needs to read the current value, add one to it, and the save that value back to the variable. That happens in this line: &lt;code&gt;counter += 1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Because the operating system knows nothing about your code and can swap threads at any point in the execution, it&amp;rsquo;s possible for this swap to happen after a thread has read the value but before it has had the chance to write it back. If the new code that is running modifies &lt;code&gt;counter&lt;/code&gt; as well, then the first thread has a stale copy of the data and trouble will ensue.&lt;/p&gt;
&lt;p&gt;As you can imagine, hitting this exact situation is fairly rare. You can run this program thousands of times and never see the problem. That&amp;rsquo;s what makes this type of problem quite difficult to debug as it can be quite hard to reproduce and can cause random-looking errors to show up.&lt;/p&gt;
&lt;p&gt;As a further example, I want to remind you that &lt;code&gt;requests.Session()&lt;/code&gt; is not thread-safe. This means that there are places where the type of interaction described above could happen if multiple threads use the same &lt;code&gt;Session&lt;/code&gt;. I bring this up not to cast aspersions on &lt;code&gt;requests&lt;/code&gt; but rather to point out that these are difficult problems to resolve.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;h3 id=&quot;asyncio-version&quot;&gt;&lt;code&gt;asyncio&lt;/code&gt; Version&lt;/h3&gt;
&lt;p&gt;Before you jump into examining the &lt;code&gt;asyncio&lt;/code&gt; example code, let&amp;rsquo;s talk more about how &lt;code&gt;asyncio&lt;/code&gt; works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;asyncio&lt;/code&gt; Basics&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This will be a simplified version of &lt;code&gt;asycio&lt;/code&gt;. There are many details that are glossed over here, but it still conveys the idea of how it works.&lt;/p&gt;
&lt;p&gt;The general concept of &lt;code&gt;asyncio&lt;/code&gt; is that a single Python object, called the event loop, controls how and when each task gets run. The event loop is aware of each task and knows what state it&amp;rsquo;s in. In reality, there are many states that tasks could be in, but for now let&amp;rsquo;s imagine a simplified event loop that just has two states.&lt;/p&gt;
&lt;p&gt;The ready state will indicate that a task has work to do and is ready to be run, and the waiting state means that the task is waiting for some external thing to finish, such as a network operation.&lt;/p&gt;
&lt;p&gt;Your simplified event loop maintains two lists of tasks, one for each of these states. It selects one of the ready tasks and starts it back to running. That task is in complete control until it cooperatively hands the control back to the event loop.&lt;/p&gt;
&lt;p&gt;When the running task gives control back to the event loop, the event loop places that task into either the ready or waiting list and then goes through each of the tasks in the waiting list to see if it has become ready by an I/O operation completing. It knows that the tasks in the ready list are still ready because it knows they haven&amp;rsquo;t run yet.&lt;/p&gt;
&lt;p&gt;Once all of the tasks have been sorted into the right list again, the event loop picks the next task to run, and the process repeats. Your simplified event loop picks the task that has been waiting the longest and runs that. This process repeats until the event loop is finished.&lt;/p&gt;
&lt;p&gt;An important point of &lt;code&gt;asyncio&lt;/code&gt; is that the tasks never give up control without intentionally doing so. They never get interrupted in the middle of an operation. This allows us to share resources a bit more easily in &lt;code&gt;asyncio&lt;/code&gt; than in &lt;code&gt;threading&lt;/code&gt;. You don&amp;rsquo;t have to worry about making your code thread-safe.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a high-level view of what&amp;rsquo;s happening with &lt;code&gt;asyncio&lt;/code&gt;. If you want more detail, &lt;a href=&quot;https://stackoverflow.com/a/51116910/6843734&quot;&gt;this StackOverflow answer&lt;/a&gt; provides some good details if you want to dig deeper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s talk about two new keywords that were added to Python: &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;. In light of the discussion above, you can view &lt;code&gt;await&lt;/code&gt; as the magic that allows the task to hand control back to the event loop. When your code awaits a function call, it&amp;rsquo;s a signal that the call is likely to be something that takes a while and that the task should give up control.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s easiest to think of &lt;code&gt;async&lt;/code&gt; as a flag to Python telling it that the function about to be defined uses &lt;code&gt;await&lt;/code&gt;. There are some cases where this is not strictly true, like &lt;a href=&quot;https://www.python.org/dev/peps/pep-0525/&quot;&gt;asynchronous generators&lt;/a&gt;, but it holds for many cases and gives you a simple model while you&amp;rsquo;re getting started.&lt;/p&gt;
&lt;p&gt;One exception to this that you&amp;rsquo;ll see in the next code is the &lt;code&gt;async with&lt;/code&gt; statement, which creates a context manager from an object you would normally await. While the semantics are a little different, the idea is the same: to flag this context manager as something that can get swapped out.&lt;/p&gt;
&lt;p&gt;As I&amp;rsquo;m sure you can imagine, there&amp;rsquo;s some complexity in managing the interaction between the event loop and the tasks. For developers starting out with &lt;code&gt;asyncio&lt;/code&gt;, these details aren&amp;rsquo;t important, but you do need to remember that any function that calls &lt;code&gt;await&lt;/code&gt; needs to be marked with &lt;code&gt;async&lt;/code&gt;. You&amp;rsquo;ll get a syntax error otherwise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Back to Code&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now that you&amp;rsquo;ve got a basic understanding of what &lt;code&gt;asyncio&lt;/code&gt; is, let&amp;rsquo;s walk through the &lt;code&gt;asyncio&lt;/code&gt; version of the example code and figure out how it works. Note that this version adds &lt;a href=&quot;https://aiohttp.readthedocs.io/en/stable/&quot;&gt;&lt;code&gt;aiohttp&lt;/code&gt;&lt;/a&gt;. You should run &lt;code&gt;pip install aiohttp&lt;/code&gt; before running 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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;aiohttp&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&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;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&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;url&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;response&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;Read &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{0}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{1}&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;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content_length&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;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aiohttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientSession&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;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tasks&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;url&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ensure_future&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&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;n&quot;&gt;tasks&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;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&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;tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;return_exceptions&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;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;n&quot;&gt;sites&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;http://www.jython.org&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://olympus.realpython.org/dice&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;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&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;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_event_loop&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;run_until_complete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&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;time&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;start_time&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;Downloaded {len(sites)} sites in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This version is a bit more complex than the previous two. It has a similar structure, but there&amp;rsquo;s a bit more work setting up the tasks than there was creating the &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;. Let&amp;rsquo;s start at the top of the example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;download_site()&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;download_site()&lt;/code&gt; at the top is almost identical to the &lt;code&gt;threading&lt;/code&gt; version with the exception of the &lt;code&gt;async&lt;/code&gt; keyword on the function definition line and the &lt;code&gt;async with&lt;/code&gt; keywords when you actually call &lt;code&gt;session.get()&lt;/code&gt;. You&amp;rsquo;ll see later why &lt;code&gt;Session&lt;/code&gt; can be passed in here rather than using thread-local storage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;download_all_sites()&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;download_all_sites()&lt;/code&gt; is where you will see the biggest change from the &lt;code&gt;threading&lt;/code&gt; example.&lt;/p&gt;
&lt;p&gt;You can share the session across all tasks, so the session is created here as a context manager. The tasks can share the session because they are all running on the same thread.  There is no way one task could interrupt another while the session is in a bad state.&lt;/p&gt;
&lt;p&gt;Inside that context manager, it creates a list of tasks using &lt;code&gt;asyncio.ensure_future()&lt;/code&gt;, which also takes care of starting them. Once all the tasks are created, this function uses &lt;code&gt;asyncio.gather()&lt;/code&gt; to keep the session context alive until all of the tasks have completed.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;threading&lt;/code&gt; code does something similar to this, but the details are conveniently handled in the &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;. There currently is not an &lt;code&gt;AsyncioPoolExecutor&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;There is one small but important change buried in the details here, however. Remember how we talked about the number of threads to create? It wasn&amp;rsquo;t obvious in the &lt;code&gt;threading&lt;/code&gt; example what the optimal number of threads was.&lt;/p&gt;
&lt;p&gt;One of the cool advantages of &lt;code&gt;asyncio&lt;/code&gt; is that it scales far better than &lt;code&gt;threading&lt;/code&gt;. Each task takes far fewer resources and less time to create than a thread, so creating and running more of them works well. This example just creates a separate task for each site to download, which works out quite well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;__main__&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Finally, the nature of &lt;code&gt;asyncio&lt;/code&gt; means that you have to start up the event loop and tell it which tasks to run. The &lt;code&gt;__main__&lt;/code&gt; section at the bottom of the file contains the code to &lt;code&gt;get_event_loop()&lt;/code&gt; and then &lt;code&gt;run_until_complete()&lt;/code&gt;. If nothing else, they&amp;rsquo;ve done an excellent job in naming those functions.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve updated to &lt;a href=&quot;https://realpython.com/python37-new-features/&quot;&gt;Python 3.7&lt;/a&gt;, the Python core developers simplified this syntax for you. Instead of the &lt;code&gt;asyncio.get_event_loop().run_until_complete()&lt;/code&gt; tongue-twister, you can just use &lt;code&gt;asyncio.run()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why the &lt;code&gt;asyncio&lt;/code&gt; Version Rocks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s really fast! In the tests on my machine, this was the fastest version of the code by a good margin:&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; ./io_asyncio.py
&lt;span class=&quot;go&quot;&gt;   [most output skipped]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Downloaded 160 in 2.5727896690368652 seconds&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The execution timing diagram looks quite similar to what&amp;rsquo;s happening in the &lt;code&gt;threading&lt;/code&gt; example. It&amp;rsquo;s just that the I/O requests are all done by the same thread:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Asyncio.31182d3731cf.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/Asyncio.31182d3731cf.png&quot; width=&quot;933&quot; height=&quot;537&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Asyncio.31182d3731cf.png&amp;amp;w=233&amp;amp;sig=eb7529b1aaefa8e02a64fbde5d58d7cc2973d1dd 233w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Asyncio.31182d3731cf.png&amp;amp;w=466&amp;amp;sig=33afb6333aba2b90a760f0fadd7cddc7de08862a 466w, https://files.realpython.com/media/Asyncio.31182d3731cf.png 933w&quot; sizes=&quot;75vw&quot; alt=&quot;Timing Diagram of a Asyncio Solution&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The lack of a nice wrapper like the &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; makes this code a bit more complex than the &lt;code&gt;threading&lt;/code&gt; example. This is a case where you have to do a little extra work to get much better performance.&lt;/p&gt;
&lt;p&gt;Also there&amp;rsquo;s a common argument that having to add &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; in the proper locations is an extra complication. To a small extent, that is true. The flip side of this argument is that it forces you to think about when a given task will get swapped out, which can help you create a better, faster, design.&lt;/p&gt;
&lt;p&gt;The scaling issue also looms large here. Running the &lt;code&gt;threading&lt;/code&gt; example above with a thread for each site is noticeably slower than running it with a handful of threads. Running the &lt;code&gt;asyncio&lt;/code&gt; example with hundreds of tasks didn&amp;rsquo;t slow it down at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Problems With the &lt;code&gt;asyncio&lt;/code&gt; Version&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are a couple of issues with &lt;code&gt;asyncio&lt;/code&gt; at this point. You need special async versions of libraries to gain the full advantage of &lt;code&gt;asycio&lt;/code&gt;. Had you just used &lt;code&gt;requests&lt;/code&gt; for downloading the sites, it would have been much slower because &lt;code&gt;requests&lt;/code&gt; is not designed to notify the event loop that it&amp;rsquo;s blocked. This issue is getting smaller and smaller as time goes on and more libraries embrace &lt;code&gt;asyncio&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another, more subtle, issue is that all of the advantages of cooperative multitasking get thrown away if one of the tasks doesn&amp;rsquo;t cooperate. A minor mistake in code can cause a task to run off and hold the processor for a long time, starving other tasks that need running. There is no way for the event loop to break in if a task does not hand control back to it.&lt;/p&gt;
&lt;p&gt;With that in mind, let&amp;rsquo;s step up to a radically different approach to concurrency, &lt;code&gt;multiprocessing&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;multiprocessing-version&quot;&gt;&lt;code&gt;multiprocessing&lt;/code&gt; Version&lt;/h3&gt;
&lt;p&gt;Unlike the previous approaches, the &lt;code&gt;multiprocessing&lt;/code&gt; version of the code takes full advantage of the multiple CPUs that your cool, new computer has. Or, in my case, that my clunky, old laptop has. Let&amp;rsquo;s start with the 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;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;multiprocessing&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;n&quot;&gt;session&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_global_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&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;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;session&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;Session&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;download_site&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;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&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;url&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;response&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;n&quot;&gt;multiprocessing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_process&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;name&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;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:Read {len(response.content)} from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{url}&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&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;multiprocessing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_global_session&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;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pool&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;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sites&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;n&quot;&gt;sites&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;http://www.jython.org&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://olympus.realpython.org/dice&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;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&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;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&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;time&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;start_time&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;Downloaded {len(sites)} in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is much shorter than the &lt;code&gt;asyncio&lt;/code&gt; example and actually looks quite similar to the &lt;code&gt;threading&lt;/code&gt; example, but before we dive into the code, let&amp;rsquo;s take a quick tour of what &lt;code&gt;multiprocessing&lt;/code&gt; does for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;multiprocessing&lt;/code&gt; in a Nutshell&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Up until this point, all of the examples of concurrency in this article run only on a single CPU or core in your computer. The reasons for this have to do with the current design of CPython and something called the Global Interpreter Lock, or GIL.&lt;/p&gt;
&lt;p&gt;This article won&amp;rsquo;t dive into the hows and whys of the &lt;a href=&quot;https://realpython.com/python-gil/&quot;&gt;GIL&lt;/a&gt;. It&amp;rsquo;s enough for now to know that the synchronous, &lt;code&gt;threading&lt;/code&gt;, and &lt;code&gt;asyncio&lt;/code&gt; versions of this example all run on a single CPU.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;multiprocessing&lt;/code&gt; in the standard library was designed to break down that barrier and run your code across multiple CPUs. At a high level, it does this by creating a new instance of the Python interpreter to run on each CPU and then farming out part of your program to run on it.&lt;/p&gt;
&lt;p&gt;As you can imagine, bringing up a separate Python interpreter is not as fast as starting a new thread in the current Python interpreter. It&amp;rsquo;s a heavyweight operation and comes with some restrictions and difficulties, but for the correct problem, it can make a huge difference.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;multiprocessing&lt;/code&gt; Code&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The code has a few small changes from our synchronous version. The first one is in &lt;code&gt;download_all_sites()&lt;/code&gt;. Instead of simply calling &lt;code&gt;download_site()&lt;/code&gt; repeatedly, it creates a &lt;code&gt;multiprocessing.Pool&lt;/code&gt; object and has it map &lt;code&gt;download_site&lt;/code&gt; to the iterable &lt;code&gt;sites&lt;/code&gt;. This should look familiar from the &lt;code&gt;threading&lt;/code&gt; example.&lt;/p&gt;
&lt;p&gt;What happens here is that the &lt;code&gt;Pool&lt;/code&gt; creates a number of separate Python interpreter processes and has each one run the specified function on some of the items in the iterable, which in our case is the list of sites. The communication between the main process and the other processes is handled by the &lt;code&gt;multiprocessing&lt;/code&gt; module for you.&lt;/p&gt;
&lt;p&gt;The line that creates &lt;code&gt;Pool&lt;/code&gt; is worth your attention. First off, it does not specify how many processes to create in the &lt;code&gt;Pool&lt;/code&gt;, although that is an optional parameter. By default, &lt;code&gt;multiprocessing.Pool()&lt;/code&gt; will determine the number of CPUs in your computer and match that. This is frequently the best answer, and it is in our case.&lt;/p&gt;
&lt;p&gt;For this problem, increasing the number of processes did not make things faster. It actually slowed things down because the cost for setting up and tearing down all those processes was larger than the benefit of doing the I/O requests in parallel.&lt;/p&gt;
&lt;p&gt;Next we have the &lt;code&gt;initializer=set_global_session&lt;/code&gt; part of that call. Remember that each process in our &lt;code&gt;Pool&lt;/code&gt; has its own memory space. That means that they cannot share things like a &lt;code&gt;Session&lt;/code&gt; object. You don&amp;rsquo;t want to create a new &lt;code&gt;Session&lt;/code&gt; each time the function is called, you want to create one for each process.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;initializer&lt;/code&gt; function parameter is built for just this case. There is not a way to pass a return value back from the &lt;code&gt;initializer&lt;/code&gt; to the function called by the process &lt;code&gt;download_site()&lt;/code&gt;, but you can initialize a global &lt;code&gt;session&lt;/code&gt; variable to hold the single session for each process. Because each process has its own memory space, the global for each one will be different.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s really all there is to it. The rest of the code is quite similar to what you&amp;rsquo;ve seen before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why the &lt;code&gt;multiprocessing&lt;/code&gt; Version Rocks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;multiprocessing&lt;/code&gt; version of this example is great because it&amp;rsquo;s relatively easy to set up and requires little extra code. It also takes full advantage of the CPU power in your computer. The execution timing diagram for this code looks like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/MProc.7cf3be371bbc.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-100&quot; src=&quot;https://files.realpython.com/media/MProc.7cf3be371bbc.png&quot; width=&quot;1893&quot; height=&quot;2097&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/MProc.7cf3be371bbc.png&amp;amp;w=473&amp;amp;sig=344e84d959d450c574483ae579b0c04ba18ffd65 473w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/MProc.7cf3be371bbc.png&amp;amp;w=946&amp;amp;sig=e7be5f8a271be230c889c1e9973ff11bbaf920be 946w, https://files.realpython.com/media/MProc.7cf3be371bbc.png 1893w&quot; sizes=&quot;75vw&quot; alt=&quot;Timing Diagram of a Multiprocessing Solution&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Problems With the &lt;code&gt;multiprocessing&lt;/code&gt; Version&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This version of the example does require some extra setup, and the global &lt;code&gt;session&lt;/code&gt; object is strange. You have to spend some time thinking about which variables will be accessed in each process.&lt;/p&gt;
&lt;p&gt;Finally, it is clearly slower than the &lt;code&gt;asyncio&lt;/code&gt; and &lt;code&gt;threading&lt;/code&gt; versions in this 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; ./io_mp.py
&lt;span class=&quot;go&quot;&gt;    [most output skipped]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Downloaded 160 in 5.718175172805786 seconds&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s not surprising, as I/O-bound problems are not really why &lt;code&gt;multiprocessing&lt;/code&gt; exists. You&amp;rsquo;ll see more as you step into the next section and look at CPU-bound examples.&lt;/p&gt;
&lt;h2 id=&quot;how-to-speed-up-a-cpu-bound-program&quot;&gt;How to Speed Up a CPU-Bound Program&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s shift gears here a little bit. The examples so far have all dealt with an I/O-bound problem. Now, you&amp;rsquo;ll look into a CPU-bound problem. As you saw, an I/O-bound problem spends most of its time waiting for external operations, like a network call, to complete. A CPU-bound problem, on the other hand, does few I/O operations, and its overall execution time is a factor of how fast it can process the required data.&lt;/p&gt;
&lt;p&gt;For the purposes of our example, we&amp;rsquo;ll use a somewhat silly function to create something that takes a long time to run on the CPU. This function computes the square of each number from 1 to the passed-in value:&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;cpu_bound&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;k&quot;&gt;return&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;n&quot;&gt;number&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;ll be passing in large numbers, so this will take a while. Remember, this is just a placeholder for your code that actually does something useful and requires significant processing time, like computing the roots of equations or sorting a large data structure.&lt;/p&gt;
&lt;h3 id=&quot;cpu-bound-synchronous-version&quot;&gt;CPU-Bound Synchronous Version&lt;/h3&gt;
&lt;p&gt;Now let&amp;rsquo;s look at the non-concurrent version of the 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;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;cpu_bound&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;k&quot;&gt;return&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;n&quot;&gt;number&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;find_sums&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&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;n&quot;&gt;cpu_bound&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;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;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;5_000_000&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;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;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&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;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;find_sums&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;duration&lt;/span&gt; &lt;span class=&quot;o&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;time&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;start_time&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;Duration &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code calls &lt;code&gt;cpu_bound()&lt;/code&gt; 20 times with a different large number each time. It does all of this on a single thread in a single process on a single CPU. The execution timing diagram looks like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/CPUBound.d2d32cb2626c.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-100&quot; src=&quot;https://files.realpython.com/media/CPUBound.d2d32cb2626c.png&quot; width=&quot;1737&quot; height=&quot;486&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CPUBound.d2d32cb2626c.png&amp;amp;w=434&amp;amp;sig=6bbdd46096cca225291e357d86691e08939744a4 434w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CPUBound.d2d32cb2626c.png&amp;amp;w=868&amp;amp;sig=88b3c863f684b8109ed17e4a013aab1e666dba1f 868w, https://files.realpython.com/media/CPUBound.d2d32cb2626c.png 1737w&quot; sizes=&quot;75vw&quot; alt=&quot;Timing Diagram of an CPU Bound Program&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Unlike the I/O-bound examples, the CPU-bound examples are usually fairly consistent in their run times. This one takes about 7.8 seconds on 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; ./cpu_non_concurrent.py
&lt;span class=&quot;go&quot;&gt;Duration 7.834432125091553 seconds&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Clearly we can do better than this. This is all running on a single CPU with no concurrency. Let&amp;rsquo;s see what we can do to make it better.&lt;/p&gt;
&lt;h3 id=&quot;threading-and-asyncio-versions&quot;&gt;&lt;code&gt;threading&lt;/code&gt; and &lt;code&gt;asyncio&lt;/code&gt; Versions&lt;/h3&gt;
&lt;p&gt;How much do you think rewriting this code using &lt;code&gt;threading&lt;/code&gt; or &lt;code&gt;asyncio&lt;/code&gt; will speed this up?&lt;/p&gt;
&lt;p&gt;If you answered &amp;ldquo;Not at all,&amp;rdquo; give yourself a cookie. If you answered, &amp;ldquo;It will slow it down,&amp;rdquo; give yourself two cookies.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s why: In your I/O-bound example above, much of the overall time was spent waiting for slow operations to finish. &lt;code&gt;threading&lt;/code&gt; and &lt;code&gt;asyncio&lt;/code&gt; sped this up by allowing you to overlap the times you were waiting instead of doing them sequentially.&lt;/p&gt;
&lt;p&gt;On a CPU-bound problem, however, there is no waiting. The CPU is cranking away as fast as it can to finish the problem. In Python, both threads and tasks run on the same CPU in the same process. That means that the one CPU is doing all of the work of the non-concurrent code plus the extra work of setting up threads or tasks. It takes more than 10 seconds:&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; ./cpu_threading.py
&lt;span class=&quot;go&quot;&gt;Duration 10.407078266143799 seconds&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I&amp;rsquo;ve written up a &lt;code&gt;threading&lt;/code&gt; version of this code and placed it with the other example code in the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/concurrency-overview&quot;&gt;GitHub repo&lt;/a&gt; so you can go test this yourself. Let&amp;rsquo;s not look at that just yet, however.&lt;/p&gt;
&lt;h3 id=&quot;cpu-bound-multiprocessing-version&quot;&gt;CPU-Bound &lt;code&gt;multiprocessing&lt;/code&gt; Version&lt;/h3&gt;
&lt;p&gt;Now you&amp;rsquo;ve finally reached where &lt;code&gt;multiprocessing&lt;/code&gt; really shines. Unlike the other concurrency libraries, &lt;code&gt;multiprocessing&lt;/code&gt; is explicitly designed to share heavy CPU workloads across multiple CPUs. Here&amp;rsquo;s what its execution timing diagram looks like:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/CPUMP.69c1a7fad9c4.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-100&quot; src=&quot;https://files.realpython.com/media/CPUMP.69c1a7fad9c4.png&quot; width=&quot;1893&quot; height=&quot;1407&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CPUMP.69c1a7fad9c4.png&amp;amp;w=473&amp;amp;sig=d831dfde47c654e01efbab1ad1368ba7dde37f2c 473w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CPUMP.69c1a7fad9c4.png&amp;amp;w=946&amp;amp;sig=f3fec21b71d848313c1d249e8fd32f01c99e1bcb 946w, https://files.realpython.com/media/CPUMP.69c1a7fad9c4.png 1893w&quot; sizes=&quot;75vw&quot; alt=&quot;Timing Diagram of a CPU-Bound Multiprocessing Solution&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what the code looks like:&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;multiprocessing&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;cpu_bound&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;k&quot;&gt;return&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;n&quot;&gt;number&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;find_sums&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;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multiprocessing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pool&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;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pool&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;cpu_bound&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;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;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;5_000_000&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;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;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&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;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;find_sums&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;duration&lt;/span&gt; &lt;span class=&quot;o&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;time&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;start_time&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;Duration &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Little of this code had to change from the non-concurrent version. You had to &lt;code&gt;import multiprocessing&lt;/code&gt; and then just change from looping through the numbers to creating a &lt;code&gt;multiprocessing.Pool&lt;/code&gt; object and using its &lt;code&gt;.map()&lt;/code&gt; method to send individual numbers to worker-processes as they become free.&lt;/p&gt;
&lt;p&gt;This was just what you did for the I/O-bound &lt;code&gt;multiprocessing&lt;/code&gt; code, but here you don&amp;rsquo;t need to worry about the &lt;code&gt;Session&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;As mentioned above, the &lt;code&gt;processes&lt;/code&gt; optional parameter to the &lt;code&gt;multiprocessing.Pool()&lt;/code&gt; constructor deserves some attention. You can specify how many &lt;code&gt;Process&lt;/code&gt; objects you want created and managed in the &lt;code&gt;Pool&lt;/code&gt;. By default, it will determine how many CPUs are in your machine and create a process for each one. While this works great for our simple example, you might want to have a little more control in a production environment.&lt;/p&gt;
&lt;p&gt;Also, as we mentioned in the first section about &lt;code&gt;threading&lt;/code&gt;, the &lt;code&gt;multiprocessing.Pool&lt;/code&gt; code is built upon building blocks like &lt;code&gt;Queue&lt;/code&gt; and &lt;code&gt;Semaphore&lt;/code&gt; that will be familiar to those of you who have done multithreaded and multiprocessing code in other languages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why the &lt;code&gt;multiprocessing&lt;/code&gt; Version Rocks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;multiprocessing&lt;/code&gt; version of this example is great because it&amp;rsquo;s relatively easy to set up and requires little extra code. It also takes full advantage of the CPU power in your computer.&lt;/p&gt;
&lt;p&gt;Hey, that&amp;rsquo;s exactly what I said the last time we looked at &lt;code&gt;multiprocessing&lt;/code&gt;. The big difference is that this time it is clearly the best option. It takes 2.5 seconds on 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; ./cpu_mp.py
&lt;span class=&quot;go&quot;&gt;Duration 2.5175397396087646 seconds&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s much better than we saw with the other options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Problems With the &lt;code&gt;multiprocessing&lt;/code&gt; Version&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are some drawbacks to using &lt;code&gt;multiprocessing&lt;/code&gt;. They don&amp;rsquo;t really show up in this simple example, but splitting your problem up so each processor can work independently can sometimes be difficult.&lt;/p&gt;
&lt;p&gt;Also, many solutions require more communication between the processes. This can add some complexity to your solution that a non-concurrent program would not need to deal with.&lt;/p&gt;
&lt;h2 id=&quot;when-to-use-concurrency&quot;&gt;When to Use Concurrency&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve covered a lot of ground here, so let&amp;rsquo;s review some of the key ideas and then discuss some decision points that will help you determine which, if any, concurrency module you want to use in your project.&lt;/p&gt;
&lt;p&gt;The first step of this process is deciding if you &lt;em&gt;should&lt;/em&gt; use a concurrency module. While the examples here make each of the libraries look pretty simple, concurrency always comes with extra complexity and can often result in bugs that are difficult to find.&lt;/p&gt;
&lt;p&gt;Hold out on adding concurrency until you have a known performance issue and &lt;em&gt;then&lt;/em&gt; determine which type of concurrency you need. As &lt;a href=&quot;https://en.wikipedia.org/wiki/Donald_Knuth&quot;&gt;Donald Knuth&lt;/a&gt; has said, &amp;ldquo;Premature optimization is the root of all evil (or at least most of it) in programming.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve decided that you should optimize your program, figuring out if your program is CPU-bound or I/O-bound is a great next step. Remember that I/O-bound programs are those that spend most of their time waiting for something to happen while CPU-bound programs spend their time processing data or crunching numbers as fast as they can.&lt;/p&gt;
&lt;p&gt;As you saw, CPU-bound problems only really gain from using &lt;code&gt;multiprocessing&lt;/code&gt;. &lt;code&gt;threading&lt;/code&gt; and &lt;code&gt;asyncio&lt;/code&gt; did not help this type of problem at all.&lt;/p&gt;
&lt;p&gt;For I/O-bound problems, there&amp;rsquo;s a general rule of thumb in the Python community: &amp;ldquo;Use &lt;code&gt;asyncio&lt;/code&gt; when you can, &lt;code&gt;threading&lt;/code&gt; when you must.&amp;rdquo; &lt;code&gt;asyncio&lt;/code&gt; can provide the best speed up for this type of program, but sometimes you will require critical libraries that have not been ported to take advantage of &lt;code&gt;asyncio&lt;/code&gt;. Remember that any task that doesn&amp;rsquo;t give up control to the event loop will block all of the other tasks.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve now seen the basic types of concurrency available in Python:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;threading&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asyncio&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;multiprocessing&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&amp;rsquo;ve got the understanding to decide which concurrency method you should use for a given problem, or if you should use any at all! In addition, you&amp;rsquo;ve achieved a better understanding of some of the problems that can arise when you&amp;rsquo;re using concurrency.&lt;/p&gt;
&lt;p&gt;I hope you&amp;rsquo;ve learned a lot from this article and that you find a great use for concurrency in your own projects!&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: A Primer</title>
      <id>https://realpython.com/django-migrations-a-primer/</id>
      <link href="https://realpython.com/django-migrations-a-primer/"/>
      <updated>2019-01-09T12:10:21+00:00</updated>
      <summary>In this tutorial, 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;You are going to learn how Django Migrations work and how you can get the most out of them over the course of four articles and one video:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1: Django Migrations: A Primer (current article)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href=&quot;https://realpython.com/digging-deeper-into-migrations/&quot;&gt;Digging Deeper into Migrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href=&quot;https://realpython.com/data-migrations/&quot;&gt;Data Migrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Video: &lt;a href=&quot;https://realpython.com/django-migrations-a-primer/#video&quot;&gt;Django 1.7 Migrations - primer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article, you&amp;rsquo;ll get comfortable with Django migrations and learn the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to create database tables without writing any SQL&lt;/li&gt;
&lt;li&gt;How to automatically modify your database after you changed your models&lt;/li&gt;
&lt;li&gt;How to revert changes made to your database&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-django-resources-learing-guide&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a free Django Learning Resources Guide (PDF)&lt;/a&gt; that shows you tips and tricks as well as common pitfalls to avoid when building Python + Django web applications.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-problems-that-migrations-solve&quot;&gt;The Problems That Migrations Solve&lt;/h2&gt;
&lt;p&gt;If you are new to Django or web development in general, you might not be familiar with the concept of database migrations, and it might not seem obvious why they&amp;rsquo;re a good idea.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s quickly define a couple of terms to make sure everybody is on the same page. Django is designed to work with a &lt;a href=&quot;https://en.wikipedia.org/wiki/Relational_database&quot;&gt;relational
database&lt;/a&gt;, stored in a relational database management system like &lt;a href=&quot;https://www.postgresql.org/&quot;&gt;PostgreSQL&lt;/a&gt;, &lt;a href=&quot;https://www.mysql.com/de/&quot;&gt;MySQL&lt;/a&gt;, or &lt;a href=&quot;https://www.sqlite.org/index.html&quot;&gt;SQLite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In a relational database, data is organized in tables. A database table has a certain number of columns, but it can have any number of rows. Each column has a specific datatype, like a string of a certain maximum length or a positive integer. The description of all tables with their columns and their respective datatypes is called a database schema.&lt;/p&gt;
&lt;p&gt;All database systems supported by Django use the language SQL to create, read, update and delete data in a relational database. SQL is also used to create, change, and delete the database tables themselves.&lt;/p&gt;
&lt;p&gt;Working directly with SQL can be quite cumbersome, so to make your life easier, Django comes with an object-relational mapper, or ORM for short. The ORM maps the relational database to the world of object oriented programming. Instead of defining database tables in SQL, you write &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/topics/db/models/&quot;&gt;Django models&lt;/a&gt; in Python. Your models define database fields, which correspond to the columns in their database tables.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of how a Django model class is mapped to a database table:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/model_to_schema.4e4b8506dc26.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/model_to_schema.4e4b8506dc26.png&quot; width=&quot;1338&quot; height=&quot;546&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/model_to_schema.4e4b8506dc26.png&amp;amp;w=334&amp;amp;sig=b5ebbf7b88386dd021e722b5f5209ec153cb15e0 334w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/model_to_schema.4e4b8506dc26.png&amp;amp;w=669&amp;amp;sig=561cc26b596e6aeb9018b67400b0843b75b3e9fe 669w, https://files.realpython.com/media/model_to_schema.4e4b8506dc26.png 1338w&quot; sizes=&quot;75vw&quot; alt=&quot;Django Model to Database Schema&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But just defining a model class in a Python file does not make a database table magically appear out of nowhere. Creating the database tables to store your Django models is the job of a database migration. Additionally, whenever you make a change to your models, like adding a field, the database has to be changed too. Migrations handle that as well.&lt;/p&gt;
&lt;p&gt;Here are a few ways Django migrations make your life easier.&lt;/p&gt;
&lt;h3 id=&quot;making-database-changes-without-sql&quot;&gt;Making Database Changes Without SQL&lt;/h3&gt;
&lt;p&gt;Without migrations, you would have to connect to your database and type in a bunch of SQL commands or use a graphical tool like &lt;a href=&quot;https://www.phpmyadmin.net/&quot;&gt;PHPMyAdmin&lt;/a&gt; to modify the database schema every time you wanted to change your model definition.&lt;/p&gt;
&lt;p&gt;In Django, migrations are primarily written in Python, so you don&amp;rsquo;t have to know any SQL unless you have really advanced use cases.&lt;/p&gt;
&lt;h3 id=&quot;avoiding-repetition&quot;&gt;Avoiding Repetition&lt;/h3&gt;
&lt;p&gt;Creating a model and then writing SQL to create the database tables for it would be repetitive.&lt;/p&gt;
&lt;p&gt;Migrations are generated from your models, making sure you &lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;don&amp;rsquo;t repeat yourself&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;ensuring-model-definitions-and-the-database-schema-in-sync&quot;&gt;Ensuring Model Definitions and the Database Schema in Sync&lt;/h3&gt;
&lt;p&gt;Usually, you have multiple instances of your database, for example one database for each developer in your team, a database for testing and a database with live data.&lt;/p&gt;
&lt;p&gt;Without migrations, you will have to perform any schema changes on each one of your database, and you will have to keep track which changes have already been made to which database.&lt;/p&gt;
&lt;p&gt;With Django Migrations, you can easily keep multiple databases in sync with your models.&lt;/p&gt;
&lt;h3 id=&quot;tracking-database-schema-change-in-version-control&quot;&gt;Tracking Database Schema Change in Version Control&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://realpython.com/python-git-github-intro/&quot;&gt;A version control system, like Git&lt;/a&gt; is excellent for code, but not so much for database schemas.&lt;/p&gt;
&lt;p&gt;As migrations are plain Python in Django, you can put them in a version control system just like any other piece of code.&lt;/p&gt;
&lt;p&gt;By now, you&amp;rsquo;re hopefully convinced that migrations are a useful and powerful tool. Let&amp;rsquo;s start learning how to unleash that power.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-a-django-project&quot;&gt;Setting Up a Django Project&lt;/h2&gt;
&lt;p&gt;Throughout this tutorial, you are going to work on a simple Bitcoin tracker app as an example project.&lt;/p&gt;
&lt;p&gt;The first step is to install Django. Here is how you do that on Linux or macOS X using a &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;virtual environment&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; python3 -m venv env
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; env/bin/activate
&lt;span class=&quot;gp&quot;&gt;(env) $&lt;/span&gt; $ pip install &lt;span class=&quot;s2&quot;&gt;&amp;quot;Django==2.1.*&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Successfully installed Django-2.1.3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you have created a new virtual environment and activated it, as well as installed Django in that virtual environment.&lt;/p&gt;
&lt;p&gt;Note that on Windows, you would run &lt;code&gt;env/bin/activate.bat&lt;/code&gt; instead of &lt;code&gt;source env/bin/activate&lt;/code&gt; to activate your virtual environment.&lt;/p&gt;
&lt;p&gt;For easier readability, console examples won&amp;rsquo;t include the &lt;code&gt;(env)&lt;/code&gt; part of the prompt from now on.&lt;/p&gt;
&lt;p&gt;With Django installed, you can create the project using the following commands:&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; django-admin.py startproject bitcoin_tracker
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; bitcoin_tracker
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py startapp historical_data
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This gives you a simple project and an app called &lt;code&gt;historical_data&lt;/code&gt;. You should now have this directory structure:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;bitcoin_tracker/
|
├── bitcoin_tracker/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
|
├── historical_data/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations/
│   │   └── __init__.py
|   |
│   ├── models.py
│   ├── tests.py
│   └── views.py
|
└── manage.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Within the &lt;code&gt;bitcoin_tracker&lt;/code&gt; directory, there are two sub-directories: &lt;code&gt;bitcoin_tracker&lt;/code&gt; for project-wide files and &lt;code&gt;historical_data&lt;/code&gt; containing files for the app you created.&lt;/p&gt;
&lt;p&gt;Now, to create a model, add this class in &lt;code&gt;historical_data/models.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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PriceHistory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auto_now_add&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;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DecimalField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_digits&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;decimal_places&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;n&quot;&gt;volume&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the basic model to keep track of Bitcoin prices.&lt;/p&gt;
&lt;p&gt;Also, don&amp;rsquo;t forget to add the newly created app to &lt;code&gt;settings.INSTALLED_APPS&lt;/code&gt;. Open &lt;code&gt;bitcoin_tracker/settings.py&lt;/code&gt; and append &lt;code&gt;historical_data&lt;/code&gt; to the list &lt;code&gt;INSTALLED_APPS&lt;/code&gt;, 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;n&quot;&gt;INSTALLED_APPS&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;django.contrib.admin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.sessions&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.messages&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.staticfiles&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;historical_data&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;The other settings are fine for this project. This tutorial assumes that your project is configured to use an SQLite database, which is the default.&lt;/p&gt;
&lt;h2 id=&quot;creating-migrations&quot;&gt;Creating Migrations&lt;/h2&gt;
&lt;p&gt;With the model created, the first thing you need to do is create a migration for it. You can do this with 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; python manage.py makemigrations historical_data
&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;historical_data&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  historical_data/migrations/0001_initial.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - Create model PriceHistory&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; Specifying the name of the application, &lt;code&gt;historical_data&lt;/code&gt;, is optional. Leaving it off creates migrations for all apps.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This creates the migrations file that instructs Django on how to create the database tables for the models defined in your application. Let&amp;rsquo;s have another look at the directory tree:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;bitcoin_tracker/
|
├── bitcoin_tracker/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
|
├── historical_data/
│   ├── migrations/
&lt;span class=&quot;hll&quot;&gt;│   │   ├── 0001_initial.py
&lt;/span&gt;│   │   └── __init__.py
|   |
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
|
├── db.sqlite3
└── manage.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the &lt;code&gt;migrations&lt;/code&gt; directory now contains a new file: &lt;code&gt;0001_initial.py&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; You might notice that running the &lt;code&gt;makemigrations&lt;/code&gt; command also created the file &lt;code&gt;db.sqlite3&lt;/code&gt;, which contains your SQLite database.&lt;/p&gt;
&lt;p&gt;When you try to access a non-existing SQLite3 database file, it will automatically be created.&lt;/p&gt;
&lt;p&gt;This behavior is unique to SQLite3. If you use any other database backend like PostgreSQL or MySQL, you have to create the database yourself &lt;em&gt;before&lt;/em&gt; running &lt;code&gt;makemigrations&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can take a peek at the database with the &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/django-admin/#dbshell&quot;&gt;&lt;code&gt;dbshell&lt;/code&gt; management command&lt;/a&gt;. In SQLite, the command to list all tables is simply &lt;code&gt;.tables&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 manage.py dbshell
&lt;span class=&quot;go&quot;&gt;SQLite version 3.19.3 2017-06-27 16:48:08&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Enter &amp;quot;.help&amp;quot; for usage hints.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sqlite&amp;gt; .tables&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sqlite&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The database is still empty. That will change when you apply the migration. Type &lt;code&gt;.quit&lt;/code&gt; to exit the SQLite shell.&lt;/p&gt;
&lt;h2 id=&quot;applying-migrations&quot;&gt;Applying Migrations&lt;/h2&gt;
&lt;p&gt;You have now created the migration, but to actually make any changes in the database, you have to apply it with the management command &lt;code&gt;migrate&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 manage.py migrate
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: admin, auth, contenttypes, historical_data, sessions&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying contenttypes.0001_initial... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying auth.0001_initial... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying admin.0001_initial... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying admin.0002_logentry_remove_auto_add... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying admin.0003_logentry_add_action_flag_choices... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying contenttypes.0002_remove_content_type_name... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying auth.0002_alter_permission_name_max_length... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying auth.0003_alter_user_email_max_length... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying auth.0004_alter_user_username_opts... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying auth.0005_alter_user_last_login_null... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying auth.0006_require_contenttypes_0002... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying auth.0007_alter_validators_add_error_messages... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying auth.0008_alter_user_username_max_length... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying auth.0009_alter_user_last_name_max_length... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying historical_data.0001_initial... OK&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying sessions.0001_initial... OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is a lot going on here! According to the output, your migration has been successfully applied. But where do all the other migrations come from?&lt;/p&gt;
&lt;p&gt;Remember the setting &lt;code&gt;INSTALLED_APPS&lt;/code&gt;? Some of the other apps listed there also come with migrations, and the &lt;code&gt;migrate&lt;/code&gt; management command applies the migrations for all installed apps by default.&lt;/p&gt;
&lt;p&gt;Have another look at the database:&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 manage.py dbshell
&lt;span class=&quot;go&quot;&gt;SQLite version 3.19.3 2017-06-27 16:48:08&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Enter &amp;quot;.help&amp;quot; for usage hints.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sqlite&amp;gt; .tables&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;auth_group                    django_admin_log&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;auth_group_permissions        django_content_type&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;auth_permission               django_migrations&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;auth_user                     django_session&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;auth_user_groups              historical_data_pricehistory&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;auth_user_user_permissions&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sqlite&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now there are multiple tables. Their names give you an idea of their purpose. The migration that you generated in the previous step has created the &lt;code&gt;historical_data_pricehistory&lt;/code&gt; table. Let&amp;rsquo;s inspect it using the &lt;code&gt;.schema&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;go&quot;&gt;sqlite&amp;gt; .schema --indent historical_data_pricehistory&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;CREATE TABLE IF NOT EXISTS &amp;quot;historical_data_pricehistory&amp;quot;(&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  &amp;quot;id&amp;quot; integer NOT NULL PRIMARY KEY AUTOINCREMENT,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  &amp;quot;date&amp;quot; datetime NOT NULL,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  &amp;quot;price&amp;quot; decimal NOT NULL,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  &amp;quot;volume&amp;quot; integer unsigned NOT NULL&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;.schema&lt;/code&gt; command prints out the &lt;code&gt;CREATE&lt;/code&gt; statement that you would execute to create the table. The parameter &lt;code&gt;--indent&lt;/code&gt; formats it nicely. Even if you are not familiar with SQL syntax, you can see that the schema of the &lt;code&gt;historical_data_pricehistory&lt;/code&gt; table reflects the fields of the &lt;code&gt;PriceHistory&lt;/code&gt; model.&lt;/p&gt;
&lt;p&gt;There is a column for each field and an additional column &lt;code&gt;id&lt;/code&gt; for the primary key, which Django creates automatically unless you explicitly specify a primary key in your model.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what happens if you run the &lt;code&gt;migrate&lt;/code&gt; command again:&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 manage.py migrate
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: admin, auth, contenttypes, historical_data, sessions&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  No migrations to apply.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nothing! Django remembers which migrations have already been applied and does not try to rerun them.&lt;/p&gt;
&lt;p&gt;It is worth noting that you can also limit the &lt;code&gt;migrate&lt;/code&gt; management command to a single app:&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 manage.py migrate historical_data
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; Apply all migrations: historical_data&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; No migrations to apply.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, Django now only applies migrations for the &lt;code&gt;historical_data&lt;/code&gt; app.&lt;/p&gt;
&lt;p&gt;When you are running the migrations for the first time, it is a good idea to apply all migrations to ensure your database contains the necessary tables for the features you might take for granted, like user authentication and sessions.&lt;/p&gt;
&lt;h2 id=&quot;changing-models&quot;&gt;Changing Models&lt;/h2&gt;
&lt;p&gt;Your models are not set in stone. Your models will change as your Django project gains more features. You might add or remove fields or change their types and options.&lt;/p&gt;
&lt;p&gt;When you change the definition of a model, the database tables used to store these models have to be changed too. If your model definitions don&amp;rsquo;t match your current database schema, you will most likely run into a &lt;code&gt;django.db.utils.OperationalError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So how do you change the database tables? By creating and applying a migration.&lt;/p&gt;
&lt;p&gt;While testing your Bitcoin tracker, you realize that you made a mistake. People are selling fractions of a Bitcoin, so the field &lt;code&gt;volume&lt;/code&gt; should be of the type &lt;code&gt;DecimalField&lt;/code&gt; instead of &lt;code&gt;PositiveIntegerField&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s change the model to look 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PriceHistory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auto_now_add&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;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DecimalField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_digits&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;decimal_places&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;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DecimalField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_digits&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;decimal_places&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&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Without migrations, you would have to figure out the SQL syntax to turn a &lt;code&gt;PositiveIntegerField&lt;/code&gt; into a &lt;code&gt;DecimalField&lt;/code&gt;. Luckily, Django will handle that for you. Just tell it to make migrations:&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 manage.py makemigrations
&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;historical_data&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  historical_data/migrations/0002_auto_20181112_1950.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - Alter field volume on pricehistory&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; The name of the migration file (&lt;code&gt;0002_auto_20181112_1950.py&lt;/code&gt;) is based on the current time and will be different if you follow along on your system.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now you apply this migration to your database:&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 manage.py migrate
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: admin, auth, contenttypes, historical_data, sessions&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying historical_data.0002_auto_20181112_1950... OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The migration has been applied successfully, so you can use &lt;code&gt;dbshell&lt;/code&gt; to verify that the changes had an effect:&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 manage.py dbshell
&lt;span class=&quot;go&quot;&gt;SQLite version 3.19.3 2017-06-27 16:48:08&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Enter &amp;quot;.help&amp;quot; for usage hints.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sqlite&amp;gt; .schema --indent historical_data_pricehistory&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;CREATE TABLE IF NOT EXISTS &amp;quot;historical_data_pricehistory&amp;quot; (&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  &amp;quot;id&amp;quot; integer NOT NULL PRIMARY KEY AUTOINCREMENT,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  &amp;quot;date&amp;quot; datetime NOT NULL,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  &amp;quot;price&amp;quot; decimal NOT NULL,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;  &amp;quot;volume&amp;quot; decimal NOT NULL&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you compare the new schema with the schema you saw earlier, you will notice that the type of the &lt;code&gt;volume&lt;/code&gt; column has changed from &lt;code&gt;integer&lt;/code&gt; to &lt;code&gt;decimal&lt;/code&gt; to reflect the change of the &lt;code&gt;volume&lt;/code&gt; field in the model from &lt;code&gt;PositiveIntegerField&lt;/code&gt; to &lt;code&gt;DecimalField&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;listing-out-migrations&quot;&gt;Listing Out Migrations&lt;/h2&gt;
&lt;p&gt;If you want to know what migrations exist in a Django project, you don&amp;rsquo;t have to dig trough the &lt;code&gt;migrations&lt;/code&gt; directories of your installed apps. You can use the &lt;code&gt;showmigrations&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; ./manage.py showmigrations
&lt;span class=&quot;go&quot;&gt;admin&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0001_initial&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0002_logentry_remove_auto_add&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0003_logentry_add_action_flag_choices&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;auth&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0001_initial&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0002_alter_permission_name_max_length&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0003_alter_user_email_max_length&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0004_alter_user_username_opts&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0005_alter_user_last_login_null&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0006_require_contenttypes_0002&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0007_alter_validators_add_error_messages&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0008_alter_user_username_max_length&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0009_alter_user_last_name_max_length&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;contenttypes&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0001_initial&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0002_remove_content_type_name&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;historical_data&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0001_initial&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0002_auto_20181112_1950&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sessions&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0001_initial&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This lists all apps in the project and the migrations associated with each app. Also, it will put a big &lt;code&gt;X&lt;/code&gt; next to the migrations that have already been applied.&lt;/p&gt;
&lt;p&gt;For our little example, the &lt;code&gt;showmigrations&lt;/code&gt; command is not particularly exciting, but it comes in handy when you start working on an existing code base or work in a team where you are not the only person adding migrations.&lt;/p&gt;
&lt;h2 id=&quot;unapplying-migrations&quot;&gt;Unapplying Migrations&lt;/h2&gt;
&lt;p&gt;Now you know how to make changes to your database schema by creating and applying migrations. At some point, you might want to undo changes and switch back to an earlier database schema because you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Want to test a migration a colleague wrote&lt;/li&gt;
&lt;li&gt;Realize that a change you made was a bad idea&lt;/li&gt;
&lt;li&gt;Work on multiple features with different database changes in parallel&lt;/li&gt;
&lt;li&gt;Want to restore a backup that was created when the database still had an older schema&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Luckily, migrations don&amp;rsquo;t have to be a one-way street. In many cases, the effects of a migration can be undone by unapplying a migration. To unapply a migration, you have to call &lt;code&gt;migrate&lt;/code&gt; with the name of the app and the name of the migration &lt;em&gt;before&lt;/em&gt; the migration you want to unapply.&lt;/p&gt;
&lt;p&gt;If you want to revert the migration &lt;code&gt;0002_auto_20181112_1950&lt;/code&gt; in your &lt;code&gt;historical_data&lt;/code&gt; app, you have to pass &lt;code&gt;0001_initial&lt;/code&gt; as an argument to the &lt;code&gt;migrate&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; python manage.py migrate historical_data 0001_initial
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Target specific migration: 0001_initial, from historical_data&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Rendering model states... DONE&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Unapplying historical_data.0002_auto_20181112_1950... OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The migration has been unapplied, meaning that the changes to the database have been reversed.&lt;/p&gt;
&lt;p&gt;Unapplying a migration does not remove its migration file. The next time you run the &lt;code&gt;migrate&lt;/code&gt; command, the migration will be applied again.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Don&amp;rsquo;t confuse unapplying migrations with the undo operation you are used to from your favorite text editor.&lt;/p&gt;
&lt;p&gt;Not all database operations can be completely reverted. If you remove a field from a model, create a migration, and apply it, Django will remove the respective column from the database.&lt;/p&gt;
&lt;p&gt;Unapplying that migration will re-create the column, but it won&amp;rsquo;t bring back the data that was stored in that column!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;When you&amp;rsquo;re dealing with migration names, Django saves you a few keystrokes by not forcing you to spell out the whole name of the migration. It needs just enough of the name to identify it uniquely.&lt;/p&gt;
&lt;p&gt;In the previous example, it would have been enough to run &lt;code&gt;python manage.py migrate historical_data 0001&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;naming-migrations&quot;&gt;Naming Migrations&lt;/h2&gt;
&lt;p&gt;In the above example, Django came up with a name for the migration based on the timestamp&amp;mdash;something like &lt;code&gt;*0002_auto_20181112_1950&lt;/code&gt;. If you&amp;rsquo;re not happy with that, then you can use the &lt;code&gt;--name&lt;/code&gt; parameter to provide a custom name (without the &lt;code&gt;.py&lt;/code&gt; extension).&lt;/p&gt;
&lt;p&gt;To try that out, you first have to remove the old migration. You have already unapplied it, so you can safely delete the 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; rm historical_data/migrations/0002_auto_20181112_1950.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you can recreate it with a more descriptive name:&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; ./manage.py makemigrations historical_data --name 0002_switch_to_decimals
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create the same migration as before except with the new name of &lt;code&gt;0002_switch_to_decimals&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You covered quite a bit of ground in this tutorial and learned the fundamentals of Django migrations.&lt;/p&gt;
&lt;p&gt;To recap, the basic steps to use Django migrations look like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create or update a model&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;./manage.py makemigrations &amp;lt;app_name&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;./manage.py migrate&lt;/code&gt; to migrate everything or &lt;code&gt;./manage.py migrate &amp;lt;app_name&amp;gt;&lt;/code&gt; to migrate an individual app&lt;/li&gt;
&lt;li&gt;Repeat as necessary&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s it! This workflow will work the majority of the time, but if things don&amp;rsquo;t work out as expected, you also know how to list and unapply migrations.&lt;/p&gt;
&lt;p&gt;If you previously created and modified your database tables with hand-written SQL, you have now become much more efficient by delegating this work to Django migrations.&lt;/p&gt;
&lt;p&gt;In the next tutorial in this series, you will dig deeper into the topic and learn &lt;a href=&quot;https://realpython.com/digging-deeper-into-migrations/&quot;&gt;how Django Migrations work under the hood&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-django-resources-learing-guide&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a free Django Learning Resources Guide (PDF)&lt;/a&gt; that shows you tips and tricks as well as common pitfalls to avoid when building Python + Django web applications.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Cheers!&lt;/p&gt;
&lt;h2 id=&quot;video&quot;&gt;Video&lt;/h2&gt;
&lt;div class=&quot;embed-responsive embed-responsive-16by9 mb-3&quot;&gt;
  &lt;iframe class=&quot;embed-responsive-item&quot; type=&quot;text/html&quot; src=&quot;https://www.youtube.com/embed/7PiyO-N6Pho?autoplay=0&amp;modestbranding=1&amp;rel=0&amp;showinfo=0&amp;origin=https://realpython.com&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&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>The Ultimate Guide to Python Type Checking</title>
      <id>https://realpython.com/python-type-checking/</id>
      <link href="https://realpython.com/python-type-checking/"/>
      <updated>2019-01-07T14:00:00+00:00</updated>
      <summary>In this guide, you&#39;ll look at Python type checking. Traditionally, types have been handled by the Python interpreter in a flexible but implicit way. Recent versions of Python allow you to specify explicit type hints that can be used by different tools to help you develop your code more efficiently.</summary>
      <content type="html">
        &lt;p&gt;In this guide, you will get a look into Python type checking. Traditionally, types have been handled by the Python interpreter in a flexible but implicit way. Recent versions of Python allow you to specify explicit type hints that can be used by different tools to help you develop your code more efficiently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial, you&amp;rsquo;ll learn about the following:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Type annotations and type hints&lt;/li&gt;
&lt;li&gt;Adding static types to code, both your code and the code of others&lt;/li&gt;
&lt;li&gt;Running a static type checker&lt;/li&gt;
&lt;li&gt;Enforcing types at runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a comprehensive guide that will cover a lot of ground. If you want to just get a quick glimpse of how type hints work in Python, and see whether type checking is something you would include in your code, you don&amp;rsquo;t need to read all of it. The two sections &lt;a href=&quot;#hello-types&quot;&gt;Hello Types&lt;/a&gt; and &lt;a href=&quot;#pros-and-cons&quot;&gt;Pros and Cons&lt;/a&gt; will give you a taste of how type checking works and recommendations about when it&amp;rsquo;ll be useful.&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;type-systems&quot;&gt;Type Systems&lt;/h2&gt;
&lt;p&gt;All programming languages include some kind of &lt;a href=&quot;https://en.wikipedia.org/wiki/Type_system&quot;&gt;type system&lt;/a&gt; that formalizes which categories of objects it can work with and how those categories are treated. For instance, a type system can define a numerical type, with &lt;code&gt;42&lt;/code&gt; as one example of an object of numerical type.&lt;/p&gt;
&lt;h3 id=&quot;dynamic-typing&quot;&gt;Dynamic Typing&lt;/h3&gt;
&lt;p&gt;Python is a dynamically typed language. This means that the Python interpreter does type checking only as code runs, and that the type of a variable is allowed to change over its lifetime. The following dummy examples demonstrate that Python has dynamic typing:&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;kc&quot;&gt;False&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;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;two&amp;quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This line never runs, so no TypeError is raised&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;mi&quot;&gt;1&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;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;two&amp;quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Now this is type checked, and a TypeError is raised&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;TypeError: unsupported operand type(s) for +: &amp;#39;int&amp;#39; and &amp;#39;str&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the first example, the branch &lt;code&gt;1 + &quot;two&quot;&lt;/code&gt; never runs so it&amp;rsquo;s never type checked. The second example shows that when &lt;code&gt;1 + &quot;two&quot;&lt;/code&gt; is evaluated it raises a &lt;code&gt;TypeError&lt;/code&gt; since you can&amp;rsquo;t add an integer and a string in Python.&lt;/p&gt;
&lt;p&gt;Next, let&amp;rsquo;s see if variables can change type:&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;thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Hello&amp;quot;&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;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;class &amp;#39;str&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;thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;28.1&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;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;class &amp;#39;float&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;type()&lt;/code&gt; returns the type of an object. These examples confirm that the type of &lt;code&gt;thing&lt;/code&gt; is allowed to change, and Python correctly infers the type as it changes.&lt;/p&gt;
&lt;h3 id=&quot;static-typing&quot;&gt;Static Typing&lt;/h3&gt;
&lt;p&gt;The opposite of dynamic typing is static typing. Static type checks are performed without running the program. In most statically typed languages, for instance C and Java, this is done as your program is compiled.&lt;/p&gt;
&lt;p&gt;With static typing, variables generally are not allowed to change types, although mechanisms for casting a variable to a different type may exist.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at a quick example from a statically typed language. Consider the following Java snippet:&lt;/p&gt;
&lt;div class=&quot;highlight java&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first line declares that the variable name &lt;code&gt;thing&lt;/code&gt; is bound to the &lt;code&gt;String&lt;/code&gt; type at compile time. The name can never be rebound to another type. In the second line, &lt;code&gt;thing&lt;/code&gt; is assigned a value. It can never be assigned a value that is not a &lt;code&gt;String&lt;/code&gt; object. For instance, if you were to later say &lt;code&gt;thing = 28.1f&lt;/code&gt; the compiler would raise an error because of incompatible types.&lt;/p&gt;
&lt;p&gt;Python will always &lt;a href=&quot;https://www.python.org/dev/peps/pep-0484/#non-goals&quot;&gt;remain a dynamically typed language&lt;/a&gt;. However, &lt;a href=&quot;https://www.python.org/dev/peps/pep-0484/&quot;&gt;PEP 484&lt;/a&gt; introduced type hints, which make it possible to also do static type checking of Python code.&lt;/p&gt;
&lt;p&gt;Unlike how types work in most other statically typed languages, type hints by themselves don&amp;rsquo;t cause Python to enforce types. As the name says, type hints just suggest types. There are other tools, which &lt;a href=&quot;#static-type-checking&quot;&gt;you&amp;rsquo;ll see later&lt;/a&gt;, that perform static type checking using type hints.&lt;/p&gt;
&lt;h3 id=&quot;duck-typing&quot;&gt;Duck Typing&lt;/h3&gt;
&lt;p&gt;Another term that is often used when talking about Python is &lt;a href=&quot;https://en.wikipedia.org/wiki/Duck_typing&quot;&gt;duck typing&lt;/a&gt;. This moniker comes from the phrase &amp;ldquo;if it walks like a duck and it quacks like a duck, then it must be a duck&amp;rdquo; (or &lt;a href=&quot;https://en.wikipedia.org/wiki/Duck_test#History&quot;&gt;any of its variations&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Duck typing is a concept related to dynamic typing, where the type or the class of an object is less important than the methods it defines. Using duck typing you do not check types at all. Instead you check for the presence of a given method or attribute.&lt;/p&gt;
&lt;p&gt;As an example, you can call &lt;code&gt;len()&lt;/code&gt; on any Python object that defines a &lt;code&gt;.__len__()&lt;/code&gt; 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TheHobbit&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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__len__&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;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;95022&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;the_hobbit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TheHobbit&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;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_hobbit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;95022&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that the call to &lt;code&gt;len()&lt;/code&gt; gives the return value of the &lt;code&gt;.__len__()&lt;/code&gt; method. In fact, the implementation of &lt;code&gt;len()&lt;/code&gt; is essentially equivalent 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&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;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__len__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In order to call &lt;code&gt;len(obj)&lt;/code&gt;, the only real constraint on &lt;code&gt;obj&lt;/code&gt; is that it must define a &lt;code&gt;.__len__()&lt;/code&gt; method. Otherwise, the object can be of types as different as &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;, or &lt;code&gt;TheHobbit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Duck typing is somewhat supported when doing static type checking of Python code, using &lt;a href=&quot;https://en.wikipedia.org/wiki/Structural_type_system&quot;&gt;structural subtyping&lt;/a&gt;. You&amp;rsquo;ll learn &lt;a href=&quot;#duck-types-and-protocols&quot;&gt;more about duck typing&lt;/a&gt; later.&lt;/p&gt;
&lt;h2 id=&quot;hello-types&quot;&gt;Hello Types&lt;/h2&gt;
&lt;p&gt;In this section you&amp;rsquo;ll see how to add type hints to a function. The following function turns a text string into a headline by adding proper capitalization and a decorative 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&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;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&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;{text.title()}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&amp;#39;-&amp;#39; * len(text)}&amp;quot;&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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; {text.title()} &amp;quot;&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;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;o&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By default the function returns the headline left aligned with an underline. By setting the &lt;code&gt;align&lt;/code&gt; flag to &lt;code&gt;False&lt;/code&gt; you can alternatively have the headline be centered with a surrounding line of &lt;code&gt;o&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Python Type Checking&lt;/span&gt;
&lt;span class=&quot;go&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;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&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;go&quot;&gt;oooooooooooooo Python Type Checking oooooooooooooo&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It&amp;rsquo;s time for our first type hints! To add information about types to the function, you simply annotate its arguments and return value as follows:&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;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&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;align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&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;o&quot;&gt;-&amp;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;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;text: str&lt;/code&gt; syntax says that the &lt;code&gt;text&lt;/code&gt; argument should be of type &lt;code&gt;str&lt;/code&gt;. Similarly, the optional &lt;code&gt;align&lt;/code&gt; argument should have type &lt;code&gt;bool&lt;/code&gt; with the default value &lt;code&gt;True&lt;/code&gt;. Finally, the &lt;code&gt;-&amp;gt; str&lt;/code&gt; notation specifies that &lt;code&gt;headline()&lt;/code&gt; will return a string.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://realpython.com/python-pep8/&quot;&gt;terms of style&lt;/a&gt;, &lt;a href=&quot;https://www.python.org/dev/peps/pep-0008/#other-recommendations&quot;&gt;PEP 8&lt;/a&gt; recommends the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use normal rules for colons, that is, no space before and one space after a colon: &lt;code&gt;text: str&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use spaces around the &lt;code&gt;=&lt;/code&gt; sign when combining an argument annotation with a default value: &lt;code&gt;align: bool = True&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use spaces around the &lt;code&gt;-&amp;gt;&lt;/code&gt; arrow: &lt;code&gt;def headline(...) -&amp;gt; str&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adding type hints like this has no runtime effect: they are only hints and are not enforced on their own. For instance, if we use a wrong type for the (admittedly badly named) &lt;code&gt;align&lt;/code&gt; argument, the code still runs without any problems or warnings:&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;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;left&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Python Type Checking&lt;/span&gt;
&lt;span class=&quot;go&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; The reason this seemingly works is that the string &lt;code&gt;&quot;left&quot;&lt;/code&gt; &lt;a href=&quot;https://realpython.com/python-operators-expressions/#evaluation-of-non-boolean-values-in-boolean-context&quot;&gt;compares as truthy&lt;/a&gt;. Using &lt;code&gt;align=&quot;center&quot;&lt;/code&gt; would not have the desired effect as &lt;code&gt;&quot;center&quot;&lt;/code&gt; is also truthy.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To catch this kind of error you can use a static type checker. That is, a tool that checks the types of your code without actually running it in the traditional sense.&lt;/p&gt;
&lt;p&gt;You might already have such a type checker built into your editor. For instance &lt;a href=&quot;https://www.jetbrains.com/pycharm/&quot;&gt;PyCharm&lt;/a&gt; immediately gives you a warning:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-66&quot; src=&quot;https://files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png&quot; width=&quot;748&quot; height=&quot;297&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png&amp;amp;w=187&amp;amp;sig=a258fbc862d03ec121a257be645b15168d1fb727 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png&amp;amp;w=374&amp;amp;sig=9510f9ce9f753dc9f55c8f9244dde031b4fb660f 374w, https://files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png 748w&quot; sizes=&quot;75vw&quot; alt=&quot;PyCharm flagging a type error&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The most common tool for doing type checking is &lt;a href=&quot;http://mypy-lang.org/&quot;&gt;Mypy&lt;/a&gt; though. You&amp;rsquo;ll get a short introduction to Mypy in a moment, while you can learn much more about how it works &lt;a href=&quot;#the-mypy-project&quot;&gt;later&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t already have Mypy on your system, you can install it using &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 mypy
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Put the following code in a file called &lt;code&gt;headlines.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;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# headlines.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&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;align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&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;o&quot;&gt;-&amp;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;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;align&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;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;{text.title()}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&amp;#39;-&amp;#39; * len(text)}&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &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; 7 &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; {text.title()} &amp;quot;&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;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;o&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;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;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;use mypy&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;center&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is essentially the same code you saw earlier: the definition of &lt;code&gt;headline()&lt;/code&gt; and two examples that are using it.&lt;/p&gt;
&lt;p&gt;Now run Mypy on this 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; mypy headlines.py
&lt;span class=&quot;go&quot;&gt;headlines.py:10: error: Argument &amp;quot;align&amp;quot; to &amp;quot;headline&amp;quot; has incompatible&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                        type &amp;quot;str&amp;quot;; expected &amp;quot;bool&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Based on the type hints, Mypy is able to tell us that we are using the wrong type on line 10.&lt;/p&gt;
&lt;p&gt;To fix the issue in the code you should change the value of the &lt;code&gt;align&lt;/code&gt; argument you are passing in. You might also rename the &lt;code&gt;align&lt;/code&gt; flag to something less confusing:&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;c1&quot;&gt;# headlines.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&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;centered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&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;lineno&quot;&gt; 4 &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;centered&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;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;{text.title()}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&amp;#39;-&amp;#39; * len(text)}&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &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; 7 &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; {text.title()} &amp;quot;&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;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;o&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;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;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;use mypy&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;centered&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;Here you&amp;rsquo;ve changed &lt;code&gt;align&lt;/code&gt; to &lt;code&gt;centered&lt;/code&gt;, and correctly used a boolean value for &lt;code&gt;centered&lt;/code&gt; when calling &lt;code&gt;headline()&lt;/code&gt;. The code now passes Mypy:&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; mypy headlines.py
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No output from Mypy means that no type errors were detected. Furthermore, when you run the code you see the expected 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; python headlines.py
&lt;span class=&quot;go&quot;&gt;Python Type Checking&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;oooooooooooooooooooo Use Mypy oooooooooooooooooooo&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first headline is aligned to the left, while the second one is centered.&lt;/p&gt;
&lt;h2 id=&quot;pros-and-cons&quot;&gt;Pros and Cons&lt;/h2&gt;
&lt;p&gt;The previous section gave you a little taste of what type checking in Python looks like. You also saw an example of one of the advantages of adding types to your code: type hints help &lt;strong&gt;catch certain errors&lt;/strong&gt;. Other advantages include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Type hints help &lt;strong&gt;document your code&lt;/strong&gt;. Traditionally, you would use &lt;a href=&quot;https://realpython.com/documenting-python-code/&quot;&gt;docstrings&lt;/a&gt; if you wanted to document the expected types of a function&amp;rsquo;s arguments. This works, but as there is no standard for docstrings (despite &lt;a href=&quot;https://www.python.org/dev/peps/pep-0257/&quot;&gt;PEP 257&lt;/a&gt; they can&amp;rsquo;t be easily used for automatic checks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type hints &lt;strong&gt;improve IDEs and linters&lt;/strong&gt;. They make it much easier to statically reason about your code. This in turn allows IDEs to offer better code completion and similar features. With the type annotation, PyCharm knows that &lt;code&gt;text&lt;/code&gt; is a string, and can give specific suggestions based on this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pycharm_code_completion.82857c2750f6.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-66&quot; src=&quot;https://files.realpython.com/media/pycharm_code_completion.82857c2750f6.png&quot; width=&quot;1122&quot; height=&quot;515&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_code_completion.82857c2750f6.png&amp;amp;w=280&amp;amp;sig=1003ed3b6f9346a44ac4690308feb53cec74b210 280w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_code_completion.82857c2750f6.png&amp;amp;w=561&amp;amp;sig=a0c5aba588155ab7faa3574a64aacbe2206a97c4 561w, https://files.realpython.com/media/pycharm_code_completion.82857c2750f6.png 1122w&quot; sizes=&quot;75vw&quot; alt=&quot;Code completion in PyCharm on a typed variable&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type hints help you &lt;strong&gt;build and maintain a cleaner architecture&lt;/strong&gt;. The act of writing type hints forces you to think about the types in your program. While the dynamic nature of Python is one of its great assets, being conscious about relying on duck typing, overloaded methods, or multiple return types is a good thing.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, static type checking is not all peaches and cream. There are also some downsides you should consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Type hints &lt;strong&gt;take developer time and effort to add&lt;/strong&gt;. Even though it probably pays off in spending less time debugging, you will spend more time entering code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type hints &lt;strong&gt;work best in modern Pythons&lt;/strong&gt;. Annotations were introduced in Python 3.0, and it&amp;rsquo;s possible to use &lt;a href=&quot;#type-comments&quot;&gt;type comments&lt;/a&gt; in Python 2.7. Still, improvements like &lt;a href=&quot;#variable-annotations&quot;&gt;variable annotations&lt;/a&gt; and &lt;a href=&quot;https://www.python.org/dev/peps/pep-0563/&quot;&gt;postponed evaluation of type hints&lt;/a&gt; mean that you&amp;rsquo;ll have a better experience doing type checks using Python 3.6 or even &lt;a href=&quot;https://realpython.com/python37-new-features/&quot;&gt;Python 3.7&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type hints &lt;strong&gt;introduce a slight penalty in start-up time&lt;/strong&gt;. If you need to use the &lt;a href=&quot;#sequences-and-mappings&quot;&gt;&lt;code&gt;typing&lt;/code&gt; module&lt;/a&gt; the import time may be significant, especially in short scripts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_cardca6bfe&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;#collapseca6bfe&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapseca6bfe&quot;&gt;Measuring Import Time&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapseca6bfe&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapseca6bfe&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapseca6bfe&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_cardca6bfe&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;You&amp;rsquo;ll &lt;a href=&quot;#sequences-and-mappings&quot;&gt;later&lt;/a&gt; learn about the &lt;code&gt;typing&lt;/code&gt; module, and how it&amp;rsquo;s necessary in most cases when you add type hints. Importing modules necessarily take some time, but how much?&lt;/p&gt;
&lt;p&gt;To get some idea about this, create two files: &lt;code&gt;empty_file.py&lt;/code&gt; should be an empty file, while &lt;code&gt;import_typing.py&lt;/code&gt; should contain the following 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;typing&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On Linux it&amp;rsquo;s quite easy to check how much time the &lt;code&gt;typing&lt;/code&gt; import takes using the &lt;a href=&quot;https://perf.wiki.kernel.org/&quot;&gt;&lt;code&gt;perf&lt;/code&gt; utility&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; perf stat -r &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt; python3.6 import_typing.py

&lt;span class=&quot;go&quot;&gt; Performance counter stats for &amp;#39;python3.6 import_typing.py&amp;#39; (1000 runs):&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt; [ ... extra information hidden for brevity ... ]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;       0.045161650 seconds time elapsed    ( +-  0.77% )&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So running the &lt;code&gt;import typing.py&lt;/code&gt; script takes about 45 milliseconds. Of course this is not all time spent on importing &lt;code&gt;typing&lt;/code&gt;. Some of this is overhead in starting the Python interpreter, so let&amp;rsquo;s compare to running Python on an empty 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; perf stat -r &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt; python3.6 empty_file.py

&lt;span class=&quot;go&quot;&gt; Performance counter stats for &amp;#39;python3.6 empty_file.py&amp;#39; (1000 runs):&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt; [ ... extra information hidden for brevity ... ]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;       0.028077845 seconds time elapsed    ( +-  0.49% )&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Based on this test, the import of the &lt;code&gt;typing&lt;/code&gt; module takes around 17 milliseconds on Python 3.6.&lt;/p&gt;
&lt;p&gt;One of the advertised improvements in Python 3.7 is &lt;a href=&quot;https://docs.python.org/3/whatsnew/3.7.html#optimizations&quot;&gt;faster startup&lt;/a&gt;. Let&amp;rsquo;s see if the results are different:&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; perf stat -r &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt; python3.7 import_typing.py
&lt;span class=&quot;go&quot;&gt; [...]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;       0.025979806 seconds time elapsed    ( +-  0.31% )&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; perf stat -r &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt; python3.7 empty_file.py
&lt;span class=&quot;go&quot;&gt; [...]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;       0.020002505 seconds time elapsed    ( +-  0.30% )&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Indeed, the general startup time is reduced by about 8 milliseconds, and the time to import &lt;code&gt;typing&lt;/code&gt; is down from 17 to around 6 milliseconds&amp;mdash;almost 3 times faster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;timeit&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are similar tools on other platforms. Python itself comes with the &lt;code&gt;timeit&lt;/code&gt; module in the standard library. Typically, we would directly use &lt;code&gt;timeit&lt;/code&gt; for the timings above. However, &lt;code&gt;timeit&lt;/code&gt; struggles to time imports reliably because Python is clever about importing modules only once. Consider the following 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; python3.6 -m timeit &lt;span class=&quot;s2&quot;&gt;&amp;quot;import typing&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;10000000 loops, best of 3: 0.134 usec per loop&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While you get a result, you should be suspicious about the result: 0.1 microsecond is more than 100000 times faster than what &lt;code&gt;perf&lt;/code&gt; measured! What &lt;code&gt;timeit&lt;/code&gt; has actually done is to run the &lt;code&gt;import typing&lt;/code&gt; statement 30 million times, with Python actually only importing &lt;code&gt;typing&lt;/code&gt; once.&lt;/p&gt;
&lt;p&gt;To get reasonable results you can tell &lt;code&gt;timeit&lt;/code&gt; to only run once:&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 -m timeit -n &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; -r &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;import typing&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1 loops, best of 1: 9.77 msec per loop&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3.7 -m timeit -n &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; -r &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;import typing&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1 loop, best of 1: 1.97 msec per loop&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These results are on the same scale as the results from &lt;code&gt;perf&lt;/code&gt; above. However, since these are based on only one execution of the code, they are not as reliable as those based on multiple runs.&lt;/p&gt;
&lt;p&gt;The conclusion in both these cases is that importing &lt;code&gt;typing&lt;/code&gt; takes a few milliseconds. For the majority of programs and scripts you write this will probably not be an issue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The New &lt;code&gt;importtime&lt;/code&gt; Option&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In Python 3.7 there is also a new command line option that can be used to figure out how much time imports take. Using &lt;code&gt;-X importtime&lt;/code&gt; you&amp;rsquo;ll get a report about all imports that are made:&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.7 -X importtime import_typing.py
&lt;span class=&quot;go&quot;&gt;import time: self [us] | cumulative | imported package&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[ ... some information hidden for brevity ... ]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;import time:       358 |        358 | zipimport&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;import time:      2107 |      14610 | site&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;import time:       272 |        272 |   collections.abc&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;import time:       664 |       3058 |   re&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;import time:      3044 |       6373 | typing&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This shows a similar result. Importing &lt;code&gt;typing&lt;/code&gt; takes about 6 milliseconds. If you&amp;rsquo;ll read the report closely you can notice that around half of this time is spent on importing the &lt;code&gt;collections.abc&lt;/code&gt; and &lt;code&gt;re&lt;/code&gt; modules which &lt;code&gt;typing&lt;/code&gt; depends on.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;So, should you use static type checking in your own code? Well, it&amp;rsquo;s not an all-or-nothing question. Luckily, Python supports the concept of &lt;a href=&quot;https://www.python.org/dev/peps/pep-0483/&quot;&gt;gradual typing&lt;/a&gt;. This means that you can gradually introduce types into your code. Code without type hints will be ignored by the static type checker. Therefore, you can start adding types to critical components, and continue as long as it adds value to you.&lt;/p&gt;
&lt;p&gt;Looking at the lists above of pros and cons you&amp;rsquo;ll notice that adding types will have no effect on your running program or the users of your program. Type checking is meant to make your life as a developer better and more convenient.&lt;/p&gt;
&lt;p&gt;A few rules of thumb on whether to add types to your project are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If you are just beginning to learn Python, you can safely wait with type hints until you have more experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type hints add little value in &lt;a href=&quot;https://www.youtube.com/watch?v=Jd8ulMb6_ls&quot;&gt;short throw-away scripts&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In libraries that will be used by others, especially ones &lt;a href=&quot;https://realpython.com/pypi-publish-python-package/&quot;&gt;published on PyPI&lt;/a&gt;, type hints add a lot of value. Other code using your libraries need these type hints to be properly type checked itself. For examples of projects using type hints see &lt;a href=&quot;https://github.com/Bogdanp/cursive_re/blob/master/cursive_re/exprs.py&quot;&gt;&lt;code&gt;cursive_re&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/ambv/black/blob/master/black.py&quot;&gt;&lt;code&gt;black&lt;/code&gt;&lt;/a&gt;, our own &lt;a href=&quot;https://github.com/realpython/reader/blob/master/reader/feed.py&quot;&gt;Real Python Reader&lt;/a&gt;, and &lt;a href=&quot;https://github.com/python/mypy/blob/master/mypy/build.py&quot;&gt;Mypy&lt;/a&gt; itself.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In bigger projects, type hints help you understand how types flow through your code, and are highly recommended. Even more so in projects where you cooperate with others.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In his excellent article &lt;a href=&quot;https://www.bernat.tech/the-state-of-type-hints-in-python/&quot;&gt;The State of Type Hints in Python&lt;/a&gt; Bernát Gábor recommends that &amp;ldquo;&lt;strong&gt;type hints should be used whenever unit tests are worth writing&lt;/strong&gt;.&amp;rdquo; Indeed, type hints play a similar role as &lt;a href=&quot;https://realpython.com/python-testing/&quot;&gt;tests&lt;/a&gt; in your code: they help you as a developer write better code.&lt;/p&gt;
&lt;p&gt;Hopefully you now have an idea about how type checking works in Python and whether it&amp;rsquo;s something you would like to employ in your own projects.&lt;/p&gt;
&lt;p&gt;In the rest of this guide, we&amp;rsquo;ll go into more detail about the Python type system, including how you run static type checkers (with particular focus on Mypy), how you type check code that uses libraries without type hints, and how you use annotations at runtime.&lt;/p&gt;
&lt;h2 id=&quot;annotations&quot;&gt;Annotations&lt;/h2&gt;
&lt;p&gt;Annotations were &lt;a href=&quot;https://www.python.org/dev/peps/pep-3107/&quot;&gt;introduced in Python 3.0&lt;/a&gt;, originally without any specific purpose. They were simply a way to associate arbitrary expressions to function arguments and return values.&lt;/p&gt;
&lt;p&gt;Years later, &lt;a href=&quot;https://www.python.org/dev/peps/pep-0484/&quot;&gt;PEP 484&lt;/a&gt; defined how to add type hints to your Python code, based off work that Jukka Lehtosalo had done on his Ph.D. project&amp;mdash;Mypy. The main way to add type hints is using annotations. As type checking is becoming more and more common, this also means that annotations should mainly be reserved for type hints.&lt;/p&gt;
&lt;p&gt;The next sections explain how annotations work in the context of type hints.&lt;/p&gt;
&lt;h3 id=&quot;function-annotations&quot;&gt;Function Annotations&lt;/h3&gt;
&lt;p&gt;For functions, you can annotate arguments and the return value. This is done as follows:&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;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default&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;return_type&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;For arguments the syntax is &lt;code&gt;argument: annotation&lt;/code&gt;, while the return type is annotated using &lt;code&gt;-&amp;gt; annotation&lt;/code&gt;. Note that the annotation must be a valid Python expression.&lt;/p&gt;
&lt;p&gt;The following simple example adds annotations to a function that calculates the circumference of a circle:&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;math&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;circumference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;float&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;nb&quot;&gt;float&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;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When running the code, you can also inspect the annotations. They are stored in a special &lt;code&gt;.__annotations__&lt;/code&gt; attribute on the 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;n&quot;&gt;circumference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;7.728317927830891&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;circumference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__annotations__&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;radius&amp;#39;: &amp;lt;class &amp;#39;float&amp;#39;&amp;gt;, &amp;#39;return&amp;#39;: &amp;lt;class &amp;#39;float&amp;#39;&amp;gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sometimes you might be confused by how Mypy is interpreting your type hints. For those cases there are special Mypy expressions: &lt;code&gt;reveal_type()&lt;/code&gt; and &lt;code&gt;reveal_locals()&lt;/code&gt;. You can add these to your code before running Mypy, and Mypy will dutifully report which types it has inferred. As an example, save the following code to &lt;code&gt;reveal.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;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# reveal.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;math&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radius&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;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circumference&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;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;reveal_locals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, run this code through Mypy:&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; mypy reveal.py
&lt;span class=&quot;go&quot;&gt;reveal.py:4: error: Revealed type is &amp;#39;builtins.float&amp;#39;&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;reveal.py:8: error: Revealed local types are:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;reveal.py:8: error: circumference: builtins.float&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;reveal.py:8: error: radius: builtins.int&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even without any annotations Mypy has correctly inferred the types of the built-in &lt;code&gt;math.pi&lt;/code&gt;, as well as our local variables &lt;code&gt;radius&lt;/code&gt; and &lt;code&gt;circumference&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 reveal expressions are only meant as a tool helping you add types and debug your type hints. If you try to run the &lt;code&gt;reveal.py&lt;/code&gt; file as a Python script it will crash with a &lt;code&gt;NameError&lt;/code&gt; since &lt;code&gt;reveal_type()&lt;/code&gt; is not a function known to the Python interpreter.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If Mypy says that &amp;ldquo;Name &amp;lsquo;&lt;code&gt;reveal_locals&lt;/code&gt;&amp;lsquo; is not defined&amp;rdquo; you might need to update your Mypy installation. The &lt;code&gt;reveal_locals()&lt;/code&gt; expression is available in &lt;a href=&quot;http://mypy-lang.blogspot.com/2018/06/mypy-0610-released.html&quot;&gt;Mypy version 0.610&lt;/a&gt; and later.&lt;/p&gt;
&lt;h3 id=&quot;variable-annotations&quot;&gt;Variable Annotations&lt;/h3&gt;
&lt;p&gt;In the definition of &lt;code&gt;circumference()&lt;/code&gt; in the previous section, you only annotated the arguments and the return value. You did not add any annotations inside the function body. More often than not, this is enough.&lt;/p&gt;
&lt;p&gt;However, sometimes the type checker needs help in figuring out the types of variables as well. Variable annotations were defined in &lt;a href=&quot;https://www.python.org/dev/peps/pep-0526/&quot;&gt;PEP 526&lt;/a&gt; and introduced in Python 3.6. The syntax is the same as for function argument 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;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.142&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;circumference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;float&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;nb&quot;&gt;float&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;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The variable &lt;code&gt;pi&lt;/code&gt; has been annotated with the &lt;code&gt;float&lt;/code&gt; type hint.&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; Static type checkers are more than able to figure out that &lt;code&gt;3.142&lt;/code&gt; is a float, so in this example the annotation of &lt;code&gt;pi&lt;/code&gt; is not necessary. As you learn more about the Python type system, you&amp;rsquo;ll see more relevant examples of variable annotations.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Annotations of variables are stored in the module level &lt;code&gt;__annotations__&lt;/code&gt; dictionary:&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;circumference&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;6.284&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__annotations__&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;pi&amp;#39;: &amp;lt;class &amp;#39;float&amp;#39;&amp;gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;re allowed to annotate a variable without giving it a value. This adds the annotation to the &lt;code&gt;__annotations__&lt;/code&gt; dictionary, while the variable remains undefined:&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;nothing&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nothing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;NameError: name &amp;#39;nothing&amp;#39; is not defined&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__annotations__&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;nothing&amp;#39;: &amp;lt;class &amp;#39;str&amp;#39;&amp;gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since no value was assigned to &lt;code&gt;nothing&lt;/code&gt;, the name &lt;code&gt;nothing&lt;/code&gt; is not yet defined.&lt;/p&gt;
&lt;h3 id=&quot;type-comments&quot;&gt;Type Comments&lt;/h3&gt;
&lt;p&gt;As mentioned, annotations were introduced in Python 3, and they&amp;rsquo;ve not been backported to Python 2. This means that if you&amp;rsquo;re writing code that needs to support &lt;a href=&quot;https://pythonclock.org/&quot;&gt;legacy Python&lt;/a&gt;, you can&amp;rsquo;t use annotations.&lt;/p&gt;
&lt;p&gt;Instead, you can use type comments. These are specially formatted comments that can be used to add type hints compatible with older code. To add type comments to a function you do 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;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;math&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;circumference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;c1&quot;&gt;# type: (float) -&amp;gt; float&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The type comments are just comments, so they can be used in any version of Python.&lt;/p&gt;
&lt;p&gt;Type comments are handled directly by the type checker, so these types are not available in the &lt;code&gt;__annotations__&lt;/code&gt; dictionary:&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;circumference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__annotations__&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A type comment must start with the &lt;code&gt;type:&lt;/code&gt; literal, and be on the same or the following line as the function definition. If you want to annotate a function with several arguments, you write each type separated by comma:&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;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&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;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill_char&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;c1&quot;&gt;# type: (str, int, str) -&amp;gt; str&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; {text.title()} &amp;quot;&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;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill_char&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;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;type comments work&amp;quot;&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;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You are also allowed to write each argument on a separate line with its own annotation:&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;c1&quot;&gt;# headlines.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;headline&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;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# type: str&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &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;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# type: int&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;fill_char&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;c1&quot;&gt;# type: str&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;# type: (...) -&amp;gt; str&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &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; {text.title()} &amp;quot;&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;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill_char&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;type comments work&amp;quot;&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;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Run the example through Python and Mypy:&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 headlines.py
&lt;span class=&quot;go&quot;&gt;---------- Type Comments Work ----------&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy headline.py
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you have errors, for instance if you happened to call &lt;code&gt;headline()&lt;/code&gt; with &lt;code&gt;width=&quot;full&quot;&lt;/code&gt; on line 10, Mypy will tell you:&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; mypy headline.py
&lt;span class=&quot;go&quot;&gt;headline.py:10: error: Argument &amp;quot;width&amp;quot; to &amp;quot;headline&amp;quot; has incompatible&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                       type &amp;quot;str&amp;quot;; expected &amp;quot;int&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also add type comments to variables. This is done similarly to how you add type comments to 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;n&quot;&gt;pi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.142&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# type: float&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, &lt;code&gt;pi&lt;/code&gt; will be type checked as a float variable.&lt;/p&gt;
&lt;h3 id=&quot;so-type-annotations-or-type-comments&quot;&gt;So, Type Annotations or Type Comments?&lt;/h3&gt;
&lt;p&gt;Should you use annotations or type comments when adding type hints to your own code? In short: &lt;strong&gt;Use annotations if you can, use type comments if you must.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Annotations provide a cleaner syntax keeping type information closer to your code. They are also the &lt;a href=&quot;https://www.python.org/dev/peps/pep-0484/&quot;&gt;officially recommended way&lt;/a&gt; of writing type hints, and will be further developed and properly maintained in the future.&lt;/p&gt;
&lt;p&gt;Type comments are more verbose and might conflict with other kinds of comments in your code like &lt;a href=&quot;https://realpython.com/python-code-quality/&quot;&gt;linter directives&lt;/a&gt;. However, they can be used in code bases that don&amp;rsquo;t support annotations.&lt;/p&gt;
&lt;p&gt;There is also hidden option number three: &lt;a href=&quot;https://github.com/python/mypy/wiki/Creating-Stubs-For-Python-Modules&quot;&gt;stub files&lt;/a&gt;. You will learn about these later, when we discuss &lt;a href=&quot;#adding-stubs&quot;&gt;adding types to third party libraries&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Stub files will work in any version of Python, at the expense of having to maintain a second set of files. In general, you only want to use stub files if you can&amp;rsquo;t change the original source code.&lt;/p&gt;
&lt;h2 id=&quot;playing-with-python-types-part-1&quot;&gt;Playing With Python Types, Part 1&lt;/h2&gt;
&lt;p&gt;Up until now you&amp;rsquo;ve only used basic types like &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, and &lt;code&gt;bool&lt;/code&gt; in your type hints. The Python type system is quite powerful, and supports many kinds of more complex types. This is necessary as it needs to be able to reasonably model Python&amp;rsquo;s dynamic duck typing nature.&lt;/p&gt;
&lt;p&gt;In this section you will learn more about this type system, while implementing a simple card game. You will see how to specify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The type of &lt;a href=&quot;#sequences-and-mappings&quot;&gt;sequences and mappings&lt;/a&gt; like tuples, lists and dictionaries&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#type-aliases&quot;&gt;Type aliases&lt;/a&gt; that make code easier to read&lt;/li&gt;
&lt;li&gt;That functions and methods &lt;a href=&quot;#functions-without-return-values&quot;&gt;do not return anything&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Objects that may be of &lt;a href=&quot;#the-any-type&quot;&gt;any type&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After a short detour into some &lt;a href=&quot;#type-theory&quot;&gt;type theory&lt;/a&gt; you will then see &lt;a href=&quot;#playing-with-python-types-part-2&quot;&gt;even more ways to specify types in Python&lt;/a&gt;. You can find the code examples from this section &lt;a href=&quot;https://github.com/realpython/materials/tree/master/python-type-checking&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;example-a-deck-of-cards&quot;&gt;Example: A Deck of Cards&lt;/h3&gt;
&lt;p&gt;The following example shows an implementation of a &lt;a href=&quot;https://en.wikipedia.org/wiki/French_playing_cards&quot;&gt;regular (French) deck of cards&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;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# game.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;random&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;n&quot;&gt;SUITS&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;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;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2 3 4 5 6 7 8 9 10 J Q K A&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;lineno&quot;&gt; 7 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&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;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Create a new deck of 52 cards&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;deck&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;s&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;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SUITS&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&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;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deal_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Deal the cards in the deck into four hands&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &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;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&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;n&quot;&gt;deck&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;4&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;lineno&quot;&gt;19 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Play a 4-player card game&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&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;lineno&quot;&gt;22 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;P1 P2 P3 P4&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;lineno&quot;&gt;23 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;hands&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;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deal_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &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;cards&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;card_str&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;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;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{s}{r}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&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;s&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;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &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;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{card_str}&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;28 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29 &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;30 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each card is represented as a tuple of strings denoting the suit and rank. The deck is represented as a list of cards. &lt;code&gt;create_deck()&lt;/code&gt; creates a regular deck of 52 playing cards, and optionally shuffles the cards. &lt;code&gt;deal_hands()&lt;/code&gt; deals the deck of cards to four players.&lt;/p&gt;
&lt;p&gt;Finally, &lt;code&gt;play()&lt;/code&gt; plays the game. As of now, it only prepares for a card game by constructing a shuffled deck and dealing cards to each player. The following is a typical 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; python game.py
&lt;span class=&quot;go&quot;&gt;P4: ♣9 ♢9 ♡2 ♢7 ♡7 ♣A ♠6 ♡K ♡5 ♢6 ♢3 ♣3 ♣Q&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P1: ♡A ♠2 ♠10 ♢J ♣10 ♣4 ♠5 ♡Q ♢5 ♣6 ♠A ♣5 ♢4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P2: ♢2 ♠7 ♡8 ♢K ♠3 ♡3 ♣K ♠J ♢A ♣7 ♡6 ♡10 ♠K&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♣2 ♣8 ♠8 ♣J ♢Q ♡9 ♡J ♠4 ♢8 ♢10 ♠9 ♡4 ♠Q&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will see how to extend this example into a more interesting game as we move along.&lt;/p&gt;
&lt;h3 id=&quot;sequences-and-mappings&quot;&gt;Sequences and Mappings&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s add type hints to our card game. In other words, let&amp;rsquo;s annotate the functions &lt;code&gt;create_deck()&lt;/code&gt;, &lt;code&gt;deal_hands()&lt;/code&gt;, and &lt;code&gt;play()&lt;/code&gt;. The first challenge is that you need to annotate composite types like the list used to represent the deck of cards and the tuples used to represent the cards themselves.&lt;/p&gt;
&lt;p&gt;With simple types like &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, and &lt;code&gt;bool&lt;/code&gt;, adding type hints is as easy as using the type itself:&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;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Guido&amp;quot;&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;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.142&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;centered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;With composite types, you are allowed to do the same:&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;names&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;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;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&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;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tuple&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;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;1&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;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;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;s2&quot;&gt;&amp;quot;centered&amp;quot;&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;s2&quot;&gt;&amp;quot;capitalize&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&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;However, this does not really tell the full story. What will be the types of &lt;code&gt;names[2]&lt;/code&gt;, &lt;code&gt;version[0]&lt;/code&gt;, and &lt;code&gt;options[&quot;centered&quot;]&lt;/code&gt;? In this concrete case you can see that they are &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, and &lt;code&gt;bool&lt;/code&gt;, respectively. However, the type hints themselves give no information about this.&lt;/p&gt;
&lt;p&gt;Instead, you should use the special types defined in the &lt;a href=&quot;https://docs.python.org/library/typing.html&quot;&gt;&lt;code&gt;typing&lt;/code&gt; module&lt;/a&gt;. These types add syntax for specifying the types of elements of composite types. You can write the following:&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;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&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;Tuple&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;names&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;nb&quot;&gt;str&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;s2&quot;&gt;&amp;quot;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&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;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&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;nb&quot;&gt;int&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;o&quot;&gt;=&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;1&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;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&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;nb&quot;&gt;bool&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;s2&quot;&gt;&amp;quot;centered&amp;quot;&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;s2&quot;&gt;&amp;quot;capitalize&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&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;Note that each of these types start with a capital letter and that they all use square brackets to define item types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;names&lt;/code&gt;&lt;/strong&gt; is a list of strings&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;&lt;/strong&gt; is a 3-tuple consisting of three integers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;options&lt;/code&gt;&lt;/strong&gt; is a dictionary mapping strings to boolean values&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;typing&lt;/code&gt; module contains many more composite types, including &lt;code&gt;Counter&lt;/code&gt;, &lt;code&gt;Deque&lt;/code&gt;, &lt;code&gt;FrozenSet&lt;/code&gt;, &lt;code&gt;NamedTuple&lt;/code&gt;, and &lt;code&gt;Set&lt;/code&gt;. In addition, the module includes other kinds of types that you&amp;rsquo;ll see in later sections.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s return to the card game. A card is represented by a tuple of two strings. You can write this as &lt;code&gt;Tuple[str, str]&lt;/code&gt;,  so the type of the deck of cards becomes &lt;code&gt;List[Tuple[str, str]]&lt;/code&gt;. Therefore you can annotate &lt;code&gt;create_deck()&lt;/code&gt; as follows:&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; 8 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&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;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;Tuple&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;nb&quot;&gt;str&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Create a new deck of 52 cards&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;deck&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;s&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;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SUITS&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&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;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In addition to the return value, you&amp;rsquo;ve also added the &lt;code&gt;bool&lt;/code&gt; type to the optional &lt;code&gt;shuffle&lt;/code&gt; argument.&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; Tuples and lists are annotated differently.&lt;/p&gt;
&lt;p&gt;A tuple is an immutable sequence, and typically consists of a fixed number of possibly differently typed elements. For example, we represent a card as a tuple of suit and rank. In general, you write &lt;code&gt;Tuple[t_1, t_2, ..., t_n]&lt;/code&gt; for an n-tuple.&lt;/p&gt;
&lt;p&gt;A list is a mutable sequence and usually consists of an unknown number of elements of the same type, for instance a list of cards. No matter how many elements are in the list there is only one type in the annotation: &lt;code&gt;List[t]&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In many cases your functions will expect some kind of &lt;a href=&quot;https://docs.python.org/glossary.html#term-sequence&quot;&gt;sequence&lt;/a&gt;, and not really care whether it is a list or a tuple. In these cases you should use &lt;code&gt;typing.Sequence&lt;/code&gt; when annotating the function argument:&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&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;elems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&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;nb&quot;&gt;float&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;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;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;elems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;Sequence&lt;/code&gt; is an example of using duck typing. A &lt;code&gt;Sequence&lt;/code&gt; is anything that supports &lt;code&gt;len()&lt;/code&gt; and &lt;code&gt;.__getitem__()&lt;/code&gt;, independent of its actual type.&lt;/p&gt;
&lt;h3 id=&quot;type-aliases&quot;&gt;Type Aliases&lt;/h3&gt;
&lt;p&gt;The type hints might become quite oblique when working with nested types like the deck of cards. You may need to stare at &lt;code&gt;List[Tuple[str, str]]&lt;/code&gt; a bit before figuring out that it matches our representation of a deck of cards.&lt;/p&gt;
&lt;p&gt;Now consider how you would annotate &lt;code&gt;deal_hands()&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;15 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deal_hands&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;deck&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;Tuple&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;nb&quot;&gt;str&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;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;Tuple&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;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&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;nb&quot;&gt;str&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;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&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;nb&quot;&gt;str&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;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&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;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &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;Tuple&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;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Deal the cards in the deck into four hands&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &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;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&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;n&quot;&gt;deck&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;4&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 just terrible!&lt;/p&gt;
&lt;p&gt;Recall that type annotations are regular Python expressions. That means that you can define your own type aliases by assigning them to new variables. You can for instance create &lt;code&gt;Card&lt;/code&gt; and &lt;code&gt;Deck&lt;/code&gt; type aliases:&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&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;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt; &lt;span class=&quot;o&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;Card&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;Card&lt;/code&gt; can now be used in type hints or in the definition of new type aliases, like &lt;code&gt;Deck&lt;/code&gt; in the example above.&lt;/p&gt;
&lt;p&gt;Using these aliases, the annotations of &lt;code&gt;deal_hands()&lt;/code&gt; become much more readable:&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;15 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deal_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Deal the cards in the deck into four hands&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &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;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&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;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Type aliases are great for making your code and its intent clearer. At the same time, these aliases can be inspected to see what they represent:&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;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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&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;Card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&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;nb&quot;&gt;str&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;Deck&lt;/span&gt; &lt;span class=&quot;o&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;Card&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;Deck&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;typing.List[typing.Tuple[str, str]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that when printing &lt;code&gt;Deck&lt;/code&gt;, it shows that it&amp;rsquo;s an alias for a list of 2-tuples of strings.&lt;/p&gt;
&lt;h3 id=&quot;functions-without-return-values&quot;&gt;Functions Without Return Values&lt;/h3&gt;
&lt;p&gt;You may know that functions without an explicit return still return &lt;code&gt;None&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_name&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;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{player_name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; plays&amp;quot;&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;ret_val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Jacob&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Jacob plays&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;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While such functions technically return something, that return value is not useful. You should add type hints saying as much by using &lt;code&gt;None&lt;/code&gt; also as the return type:&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;c1&quot;&gt;# play.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_name&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;o&quot;&gt;-&amp;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;lineno&quot;&gt; 4 &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;{player_name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; plays&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;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret_val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Filip&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The annotations help catch the kinds of subtle bugs where you are trying to use a meaningless return value. Mypy will give you a helpful warning:&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; mypy play.py
&lt;span class=&quot;go&quot;&gt;play.py:6: error: &amp;quot;play&amp;quot; does not return a value&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that being explicit about a function not returning anything is different from not adding a type hint about the return value:&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;# play.py&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_name&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;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;{player_name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; plays&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ret_val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Henrik&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this latter case Mypy has no information about the return value so it will not generate any warning:&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; mypy play.py
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a more exotic case, note that you can also annotate functions that are never expected to return normally. This is done using &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/more_types.html#the-noreturn-type&quot;&gt;&lt;code&gt;NoReturn&lt;/code&gt;&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;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;NoReturn&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;black_hole&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;NoReturn&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;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;There is no going back ...&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;code&gt;black_hole()&lt;/code&gt; always raises an exception, it will never return properly.&lt;/p&gt;
&lt;h3 id=&quot;example-play-some-cards&quot;&gt;Example: Play Some Cards&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s return to our &lt;a href=&quot;#example-a-deck-of-cards&quot;&gt;card game example&lt;/a&gt;. In this second version of the game, we deal a hand of cards to each player as before. Then a start player is chosen and the players take turns playing their cards. There are not really any rules in the game though, so the players will just play random cards:&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;c1&quot;&gt;# game.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;random&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SUITS&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;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;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2 3 4 5 6 7 8 9 10 J Q K A&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;lineno&quot;&gt; 8 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&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;nb&quot;&gt;str&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;Deck&lt;/span&gt; &lt;span class=&quot;o&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;Card&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;lineno&quot;&gt;12 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Create a new deck of 52 cards&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;deck&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;s&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;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SUITS&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&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;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deal_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Deal the cards in the deck into four hands&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &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;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&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;n&quot;&gt;deck&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;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Choose and return a random item&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &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;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Rotate player order so that start goes first&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&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;lineno&quot;&gt;30 &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;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;31 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;o&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;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;32 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;33 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;34 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&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;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Play a 4-player card game&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&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;lineno&quot;&gt;37 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;P1 P2 P3 P4&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;lineno&quot;&gt;38 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;hands&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;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deal_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;39 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;start_player&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;40 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;turn_order&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;n&quot;&gt;start_player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;41 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;42 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Randomly play cards from each player&amp;#39;s hand until empty&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;43 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;44 &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;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turn_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;45 &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hands&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;46 &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;hands&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;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;47 &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;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: {card[0] + card[1]:&amp;lt;3}  &amp;quot;&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;lineno&quot;&gt;48 &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;lineno&quot;&gt;49 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;50 &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;51 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that in addition to changing &lt;code&gt;play()&lt;/code&gt;, we have added two new functions that need type hints: &lt;code&gt;choose()&lt;/code&gt; and &lt;code&gt;player_order()&lt;/code&gt;. Before discussing how we&amp;rsquo;ll add type hints to them, here is an example output from running the game:&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 game.py
&lt;span class=&quot;go&quot;&gt;P3: ♢10  P4: ♣4   P1: ♡8   P2: ♡Q&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♣8   P4: ♠6   P1: ♠5   P2: ♡K&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♢9   P4: ♡J   P1: ♣A   P2: ♡A&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♠Q   P4: ♠3   P1: ♠7   P2: ♠A&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♡4   P4: ♡6   P1: ♣2   P2: ♠K&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♣K   P4: ♣7   P1: ♡7   P2: ♠2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♣10  P4: ♠4   P1: ♢5   P2: ♡3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♣Q   P4: ♢K   P1: ♣J   P2: ♡9&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♢2   P4: ♢4   P1: ♠9   P2: ♠10&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♢A   P4: ♡5   P1: ♠J   P2: ♢Q&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♠8   P4: ♢7   P1: ♢3   P2: ♢J&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♣3   P4: ♡10  P1: ♣9   P2: ♡2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;P3: ♢6   P4: ♣6   P1: ♣5   P2: ♢8&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, player &lt;code&gt;P3&lt;/code&gt; was randomly chosen as the starting player. In turn, each player plays a card: first &lt;code&gt;P3&lt;/code&gt;, then &lt;code&gt;P4&lt;/code&gt;, then &lt;code&gt;P1&lt;/code&gt;, and finally &lt;code&gt;P2&lt;/code&gt;. The players keep playing cards as long as they have any left in their hand.&lt;/p&gt;
&lt;h3 id=&quot;the-any-type&quot;&gt;The &lt;code&gt;Any&lt;/code&gt; Type&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;choose()&lt;/code&gt; works for both lists of names and lists of cards (and any other sequence for that matter). One way to add type hints for this would be 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;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;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;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Any&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;Any&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;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;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means more or less what it says: &lt;code&gt;items&lt;/code&gt; is a sequence that can contain items of any type and &lt;code&gt;choose()&lt;/code&gt; will return one such item of any type. Unfortunately, this is not that useful. Consider the following 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;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# choose.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;random&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &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;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Any&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;Any&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;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;items&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;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&amp;quot;&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;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;lineno&quot;&gt;12 &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;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;reveal_type&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;While Mypy will correctly infer that &lt;code&gt;names&lt;/code&gt; is a list of strings, that information is lost after the call to &lt;code&gt;choose()&lt;/code&gt; because of the use of the &lt;code&gt;Any&lt;/code&gt; type:&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; mypy choose.py
&lt;span class=&quot;go&quot;&gt;choose.py:10: error: Revealed type is &amp;#39;builtins.list[builtins.str*]&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose.py:13: error: Revealed type is &amp;#39;Any&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ll see a better way shortly. First though, let&amp;rsquo;s have a more theoretical look at the Python type system, and the special role &lt;code&gt;Any&lt;/code&gt; plays.&lt;/p&gt;
&lt;h2 id=&quot;type-theory&quot;&gt;Type Theory&lt;/h2&gt;
&lt;p&gt;This tutorial is mainly a practical guide and we will only scratch the surface of the theory underpinning Python type hints. For more details &lt;a href=&quot;https://www.python.org/dev/peps/pep-0483/&quot;&gt;PEP 483&lt;/a&gt; is a good starting point. If you want to get back to the practical examples, feel free to &lt;a href=&quot;#playing-with-python-types-part-2&quot;&gt;skip to the next section&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;subtypes&quot;&gt;Subtypes&lt;/h3&gt;
&lt;p&gt;One important concept is that of &lt;strong&gt;subtypes&lt;/strong&gt;. Formally, we say that a type &lt;code&gt;T&lt;/code&gt; is a subtype of &lt;code&gt;U&lt;/code&gt; if the following two conditions hold:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every value from &lt;code&gt;T&lt;/code&gt; is also in the set of values of &lt;code&gt;U&lt;/code&gt; type.&lt;/li&gt;
&lt;li&gt;Every function from &lt;code&gt;U&lt;/code&gt; type is also in the set of functions of &lt;code&gt;T&lt;/code&gt; type.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two conditions guarantees that even if type &lt;code&gt;T&lt;/code&gt; is different from &lt;code&gt;U&lt;/code&gt;, variables of type &lt;code&gt;T&lt;/code&gt; can always pretend to be &lt;code&gt;U&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For a concrete example, consider &lt;code&gt;T = bool&lt;/code&gt; and &lt;code&gt;U = int&lt;/code&gt;. The &lt;code&gt;bool&lt;/code&gt; type takes only two values. Usually these are denoted &lt;code&gt;True&lt;/code&gt; and &lt;code&gt;False&lt;/code&gt;, but these names are just aliases for the integer values &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;0&lt;/code&gt;, respectively:&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;int&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;go&quot;&gt;0&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;int&lt;/span&gt;&lt;span class=&quot;p&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;1&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&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;go&quot;&gt;2&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;issubclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;nb&quot;&gt;int&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;Since 0 and 1 are both integers, the first condition holds. Above you can see that booleans can be added together, but they can also do anything else integers can. This is the second condition above. In other words, &lt;code&gt;bool&lt;/code&gt; is a subtype of &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The importance of subtypes is that a subtype can always pretend to be its supertype. For instance, the following code type checks as correct:&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;double&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;o&quot;&gt;-&amp;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;k&quot;&gt;return&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;mi&quot;&gt;2&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;double&lt;/span&gt;&lt;span class=&quot;p&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;c1&quot;&gt;# Passing in bool instead of int&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Subtypes are somewhat related to subclasses. In fact all subclasses corresponds to subtypes, and &lt;code&gt;bool&lt;/code&gt; is a subtype of &lt;code&gt;int&lt;/code&gt; because &lt;code&gt;bool&lt;/code&gt; is a subclass of &lt;code&gt;int&lt;/code&gt;. However, there are also subtypes that do not correspond to subclasses. For instance &lt;code&gt;int&lt;/code&gt; is a subtype of &lt;code&gt;float&lt;/code&gt;, but &lt;code&gt;int&lt;/code&gt; is not a subclass of &lt;code&gt;float&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;covariant-contravariant-and-invariant&quot;&gt;Covariant, Contravariant, and Invariant&lt;/h3&gt;
&lt;p&gt;What happens when you use subtypes inside composite types? For instance, is &lt;code&gt;Tuple[bool]&lt;/code&gt; a subtype of &lt;code&gt;Tuple[int]&lt;/code&gt;? The answer depends on the composite type, and whether that type is &lt;a href=&quot;https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29&quot;&gt;covariant, contravariant, or invariant&lt;/a&gt;. This gets technical fast, so let&amp;rsquo;s just give a few examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Tuple&lt;/code&gt; is covariant. This means that it preserves the type hierarchy of its item types: &lt;code&gt;Tuple[bool]&lt;/code&gt; is a subtype of &lt;code&gt;Tuple[int]&lt;/code&gt; because &lt;code&gt;bool&lt;/code&gt; is a subtype of &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;List&lt;/code&gt; is invariant. Invariant types give no guarantee about subtypes. While all values of &lt;code&gt;List[bool]&lt;/code&gt; are values of &lt;code&gt;List[int]&lt;/code&gt;, you can append an &lt;code&gt;int&lt;/code&gt; to &lt;code&gt;List[int]&lt;/code&gt; and not to &lt;code&gt;List[bool]&lt;/code&gt;. In other words, the second condition for subtypes does not hold, and &lt;code&gt;List[bool]&lt;/code&gt; is not a subtype of &lt;code&gt;List[int]&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Callable&lt;/code&gt; is contravariant in its arguments. This means that it reverses the type hierarchy. You will see how &lt;code&gt;Callable&lt;/code&gt; works &lt;a href=&quot;#callables&quot;&gt;later&lt;/a&gt;, but for now think of &lt;code&gt;Callable[[T], ...]&lt;/code&gt; as a function with its only argument being of type &lt;code&gt;T&lt;/code&gt;. An example of a &lt;code&gt;Callable[[int], ...]&lt;/code&gt; is the &lt;code&gt;double()&lt;/code&gt; function defined above. Being contravariant means that if a function operating on a &lt;code&gt;bool&lt;/code&gt; is expected, then a function operating on an &lt;code&gt;int&lt;/code&gt; would be acceptable.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In general, you don&amp;rsquo;t need to keep these expression straight. However, you should be aware that subtypes and composite types may not be simple and intuitive.&lt;/p&gt;
&lt;h3 id=&quot;gradual-typing-and-consistent-types&quot;&gt;Gradual Typing and Consistent Types&lt;/h3&gt;
&lt;p&gt;Earlier we mentioned that Python supports &lt;a href=&quot;http://wphomes.soic.indiana.edu/jsiek/what-is-gradual-typing/&quot;&gt;gradual typing&lt;/a&gt;, where you can gradually add type hints to your Python code. Gradual typing is essentially made possible by the &lt;code&gt;Any&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;Somehow &lt;code&gt;Any&lt;/code&gt; sits both at the top and at the bottom of the type hierarchy of subtypes. Any type behaves as if it is a subtype of &lt;code&gt;Any&lt;/code&gt;, and &lt;code&gt;Any&lt;/code&gt; behaves as if it is a subtype of any other type. Looking at the definition of subtypes above this is not really possible. Instead we talk about &lt;strong&gt;consistent types&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The type &lt;code&gt;T&lt;/code&gt; is consistent with the type &lt;code&gt;U&lt;/code&gt; if &lt;code&gt;T&lt;/code&gt; is a subtype of &lt;code&gt;U&lt;/code&gt; or either &lt;code&gt;T&lt;/code&gt; or &lt;code&gt;U&lt;/code&gt; is &lt;code&gt;Any&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The type checker only complains about inconsistent types. The takeaway is therefore that you will never see type errors arising from the &lt;code&gt;Any&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;This means that you can use &lt;code&gt;Any&lt;/code&gt; to explicitly fall back to dynamic typing, describe types that are too complex to describe in the Python type system, or describe items in composite types. For instance, a dictionary with string keys that can take any type as its values can be annotated &lt;code&gt;Dict[str, Any]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Do remember, though, if you use &lt;code&gt;Any&lt;/code&gt; the static type checker will effectively not do any type any checking.&lt;/p&gt;
&lt;h2 id=&quot;playing-with-python-types-part-2&quot;&gt;Playing With Python Types, Part 2&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s return to our practical examples. Recall that you were trying to annotate the general &lt;code&gt;choose()&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;random&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;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Any&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;Any&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;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;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The problem with using &lt;code&gt;Any&lt;/code&gt; is that you are needlessly losing type information. You know that if you pass a list of strings to &lt;code&gt;choose()&lt;/code&gt;, it will return a string. Below you&amp;rsquo;ll see how to express this using type variables, as well as how to work with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#duck-types-and-protocols&quot;&gt;Duck types and protocols&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Arguments with &lt;a href=&quot;#the-optional-type&quot;&gt;&lt;code&gt;None&lt;/code&gt; as default value&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#type-hints-for-methods&quot;&gt;Class methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#classes-as-types&quot;&gt;The type of your own classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#annotating-args-and-kwargs&quot;&gt;Variable number of arguments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;type-variables&quot;&gt;Type Variables&lt;/h3&gt;
&lt;p&gt;A type variable is a special variable that can take on any type, depending on the situation.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s create a type variable that will effectively encapsulate the behavior of &lt;code&gt;choose()&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;c1&quot;&gt;# choose.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;random&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &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;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Choosable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Chooseable&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Choosable&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;Choosable&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;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;items&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;names&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;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&amp;quot;&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;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;lineno&quot;&gt;14 &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;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;reveal_type&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;A type variable must be defined using &lt;code&gt;TypeVar&lt;/code&gt; from the &lt;code&gt;typing&lt;/code&gt; module. When used, a type variable ranges over all possible types and takes the most specific type possible. In the example, &lt;code&gt;name&lt;/code&gt; is now a &lt;code&gt;str&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; mypy choose.py
&lt;span class=&quot;go&quot;&gt;choose.py:12: error: Revealed type is &amp;#39;builtins.list[builtins.str*]&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose.py:15: error: Revealed type is &amp;#39;builtins.str*&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Consider a few other examples:&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;c1&quot;&gt;# choose_examples.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;choose&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;choose&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;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&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;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&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;2&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;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&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;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.14&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;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Python&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;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first two examples should have type &lt;code&gt;str&lt;/code&gt; and &lt;code&gt;int&lt;/code&gt;, but what about the last two? The individual list items have different types, and in that case the &lt;code&gt;Choosable&lt;/code&gt; type variable does its best to accommodate:&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; mypy choose_examples.py
&lt;span class=&quot;go&quot;&gt;choose_examples.py:5: error: Revealed type is &amp;#39;builtins.str*&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose_examples.py:6: error: Revealed type is &amp;#39;builtins.int*&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose_examples.py:7: error: Revealed type is &amp;#39;builtins.float*&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose_examples.py:8: error: Revealed type is &amp;#39;builtins.object*&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you&amp;rsquo;ve already seen &lt;code&gt;bool&lt;/code&gt; is a subtype of &lt;code&gt;int&lt;/code&gt;, which again is a subtype of &lt;code&gt;float&lt;/code&gt;. So in the third example the return value of &lt;code&gt;choose()&lt;/code&gt; is guaranteed to be something that can be thought of as a &lt;code&gt;float&lt;/code&gt;. In the last example, there is no subtype relationship between &lt;code&gt;str&lt;/code&gt; and &lt;code&gt;int&lt;/code&gt;, so the best that can be said about the return value is that it is an object.&lt;/p&gt;
&lt;p&gt;Note that none of these examples raised a type error. Is there a way to tell the type checker that &lt;code&gt;choose()&lt;/code&gt; should accept both strings and numbers, but not both at the same time?&lt;/p&gt;
&lt;p&gt;You can constrain type variables by listing the acceptable types:&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;c1&quot;&gt;# choose.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;random&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &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;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;Choosable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Choosable&amp;quot;&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;nb&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Choosable&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;Choosable&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;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;items&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;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&amp;quot;&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;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&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;2&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;lineno&quot;&gt;13 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&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;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.14&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;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Python&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;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now &lt;code&gt;Choosable&lt;/code&gt; can only be either &lt;code&gt;str&lt;/code&gt; or &lt;code&gt;float&lt;/code&gt;, and Mypy will note that the last example is an error:&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; mypy choose.py
&lt;span class=&quot;go&quot;&gt;choose.py:11: error: Revealed type is &amp;#39;builtins.str*&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose.py:12: error: Revealed type is &amp;#39;builtins.float*&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose.py:13: error: Revealed type is &amp;#39;builtins.float*&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose.py:14: error: Revealed type is &amp;#39;builtins.object*&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose.py:14: error: Value of type variable &amp;quot;Choosable&amp;quot; of &amp;quot;choose&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                     cannot be &amp;quot;object&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Also note that in the second example the type is considered &lt;code&gt;float&lt;/code&gt; even though the input list only contains &lt;code&gt;int&lt;/code&gt; objects. This is because &lt;code&gt;Choosable&lt;/code&gt; was restricted to strings and floats and &lt;code&gt;int&lt;/code&gt; is a subtype of &lt;code&gt;float&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In our card game we want to restrict &lt;code&gt;choose()&lt;/code&gt; to be used for &lt;code&gt;str&lt;/code&gt; and &lt;code&gt;Card&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;Choosable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Choosable&amp;quot;&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;Card&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;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Choosable&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;Choosable&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;We briefly mentioned that &lt;code&gt;Sequence&lt;/code&gt; represents both lists and tuples. As we noted, a &lt;code&gt;Sequence&lt;/code&gt; can be thought of as a duck type, since it can be any object with &lt;code&gt;.__len__()&lt;/code&gt; and &lt;code&gt;.__getitem__()&lt;/code&gt; implemented.&lt;/p&gt;
&lt;h3 id=&quot;duck-types-and-protocols&quot;&gt;Duck Types and Protocols&lt;/h3&gt;
&lt;p&gt;Recall the following example from &lt;a href=&quot;#duck-typing&quot;&gt;the introduction&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&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;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__len__&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;len()&lt;/code&gt; can return the length of any object that has implemented the &lt;code&gt;.__len__()&lt;/code&gt; method. How can we add type hints to &lt;code&gt;len()&lt;/code&gt;, and in particular the &lt;code&gt;obj&lt;/code&gt; argument?&lt;/p&gt;
&lt;p&gt;The answer hides behind the academic sounding term &lt;a href=&quot;https://en.wikipedia.org/wiki/Structural_type_system&quot;&gt;structural subtyping&lt;/a&gt;. One way to categorize type systems is by whether they are &lt;strong&gt;nominal&lt;/strong&gt; or &lt;strong&gt;structural&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In a &lt;strong&gt;nominal&lt;/strong&gt; system, comparisons between types are based on names and declarations. The Python type system is mostly nominal, where an &lt;code&gt;int&lt;/code&gt; can be used in place of a &lt;code&gt;float&lt;/code&gt; because of their subtype relationship.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In a &lt;strong&gt;structural&lt;/strong&gt; system, comparisons between types are based on structure. You could define a structural type &lt;code&gt;Sized&lt;/code&gt; that includes all instances that define &lt;code&gt;.__len__()&lt;/code&gt;, irrespective of their nominal type.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is ongoing work to bring a full-fledged structural type system to Python through &lt;a href=&quot;https://www.python.org/dev/peps/pep-0544/&quot;&gt;PEP 544&lt;/a&gt; which aims at adding a concept called protocols. Most of PEP 544 is already &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/protocols.html&quot;&gt;implemented in Mypy&lt;/a&gt; though.&lt;/p&gt;
&lt;p&gt;A protocol specifies one or more methods that must be implemented. For example, all classes defining &lt;code&gt;.__len__()&lt;/code&gt; fulfill the &lt;code&gt;typing.Sized&lt;/code&gt; protocol. We can therefore annotate &lt;code&gt;len()&lt;/code&gt; as follows:&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;Sized&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sized&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;nb&quot;&gt;int&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;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__len__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Other &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/protocols.html#predefined-protocols&quot;&gt;examples of protocols&lt;/a&gt; defined in the &lt;code&gt;typing&lt;/code&gt; module include &lt;code&gt;Container&lt;/code&gt;, &lt;code&gt;Iterable&lt;/code&gt;, &lt;code&gt;Awaitable&lt;/code&gt;, and &lt;code&gt;ContextManager&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also define your own protocols. This is done by inheriting from &lt;code&gt;Protocol&lt;/code&gt; and defining the function signatures (with empty function bodies) that the protocol expects. The following example shows how &lt;code&gt;len()&lt;/code&gt; and &lt;code&gt;Sized&lt;/code&gt; could have been implemented:&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_extensions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Protocol&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Protocol&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;__len__&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;nb&quot;&gt;int&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;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sized&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;nb&quot;&gt;int&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;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__len__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At the time of writing the support for self-defined protocols is still experimental and only available through the &lt;code&gt;typing_extensions&lt;/code&gt; module. This module must be explicitly installed from &lt;a href=&quot;https://pypi.org/project/typing-extensions/&quot;&gt;PyPI&lt;/a&gt; by doing &lt;code&gt;pip install typing-extensions&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-optional-type&quot;&gt;The &lt;code&gt;Optional&lt;/code&gt; Type&lt;/h3&gt;
&lt;p&gt;A common pattern in Python is to use &lt;code&gt;None&lt;/code&gt; as a default value for an argument. This is usually done either to avoid problems with &lt;a href=&quot;https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html&quot;&gt;mutable default values&lt;/a&gt; or to have a sentinel value flagging special behavior.&lt;/p&gt;
&lt;p&gt;In the card example, the &lt;code&gt;player_order()&lt;/code&gt; function uses &lt;code&gt;None&lt;/code&gt; as a sentinel value for &lt;code&gt;start&lt;/code&gt; saying that if no start player is given it should be chosen randomly:&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;27 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Rotate player order so that start goes first&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&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;lineno&quot;&gt;30 &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;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;31 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;o&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;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;32 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The challenge this creates for type hinting is that in general &lt;code&gt;start&lt;/code&gt; should be a string. However, it may also take the special non-string value &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In order to annotate such arguments you can use the &lt;code&gt;Optional&lt;/code&gt; type:&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;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&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;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&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;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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&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;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;Optional&lt;/code&gt; type simply says that a variable either has the type specified or is &lt;code&gt;None&lt;/code&gt;. An equivalent way of specifying the same would be using the &lt;code&gt;Union&lt;/code&gt; type: &lt;code&gt;Union[None, str]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Note that when using either &lt;code&gt;Optional&lt;/code&gt; or &lt;code&gt;Union&lt;/code&gt; you must take care that the variable has the correct type when you operate on it. This is done in the example by testing whether &lt;code&gt;start is None&lt;/code&gt;. Not doing so would cause both static type errors as well as possible runtime errors:&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;c1&quot;&gt;# player_order.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &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;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&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;player_order&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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&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;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &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;Sequence&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;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;o&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;start&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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Mypy tells you that you have not taken care of the case where &lt;code&gt;start&lt;/code&gt; is &lt;code&gt;None&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; mypy player_order.py
&lt;span class=&quot;go&quot;&gt;player_order.py:8: error: Argument 1 to &amp;quot;index&amp;quot; of &amp;quot;list&amp;quot; has incompatible&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          type &amp;quot;Optional[str]&amp;quot;; expected &amp;quot;str&amp;quot;&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; The use of &lt;code&gt;None&lt;/code&gt; for optional arguments is so common that Mypy handles it automatically. Mypy assumes that a default argument of &lt;code&gt;None&lt;/code&gt; indicates an optional argument even if the type hint does not explicitly say so. You could have used 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&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;start&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;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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&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;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you don&amp;rsquo;t want Mypy to make this assumption you can turn it off with the &lt;code&gt;--no-implicit-optional&lt;/code&gt; command line option.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;example-the-objective-of-the-game&quot;&gt;Example: The Object(ive) of the Game&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s rewrite the card game to be more &lt;a href=&quot;https://realpython.com/python3-object-oriented-programming/&quot;&gt;object-oriented&lt;/a&gt;. This will allow us to discuss how to properly annotate classes and methods.&lt;/p&gt;
&lt;p&gt;A more or less direct translation of our card game into code that uses classes for &lt;code&gt;Card&lt;/code&gt;, &lt;code&gt;Deck&lt;/code&gt;, &lt;code&gt;Player&lt;/code&gt;, and &lt;code&gt;Game&lt;/code&gt; looks something like 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;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# game.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;random&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &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;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Card&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;SUITS&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;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;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2 3 4 5 6 7 8 9 10 J Q K A&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;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;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;suit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rank&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &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;rank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &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;lineno&quot;&gt;15 &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;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.suit}{self.rank}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Deck&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;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;cards&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&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;lineno&quot;&gt;23 &lt;/span&gt;        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Create a new deck of 52 cards&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;cards&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;Card&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&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;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SUITS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &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;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deal&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;num_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;30 &lt;/span&gt;        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Deal the cards in the deck into a number of hands&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;31 &lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;cls&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;vm&quot;&gt;__class__&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;32 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&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;cards&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_hands&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;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;n&quot;&gt;num_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;33 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;34 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35 &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&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36 &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&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;lineno&quot;&gt;37 &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;hand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;38 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;39 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play_card&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;40 &lt;/span&gt;        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Play a card from the player&amp;#39;s hand&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;41 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;card&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;choice&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;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;42 &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;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;43 &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;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{card!r:&amp;lt;3}&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;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;lineno&quot;&gt;44 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;45 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;46 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;47 &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;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;48 &lt;/span&gt;        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Set up the deck and deal cards to 4 players&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;49 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&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;lineno&quot;&gt;50 &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;names&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;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;s2&quot;&gt;&amp;quot;P1 P2 P3 P4&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;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;51 &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;hands&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;lineno&quot;&gt;52 &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&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;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deal&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;lineno&quot;&gt;53 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;54 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;55 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&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;56 &lt;/span&gt;        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Play a card game&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;57 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;start_player&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;choice&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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;58 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;turn_order&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;player_order&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;n&quot;&gt;start_player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;59 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;60 &lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# Play cards from each player&amp;#39;s hand until empty&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;61 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;while&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;hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_player&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;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;62 &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;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turn_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;63 &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;hands&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;play_card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;64 &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;lineno&quot;&gt;65 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;66 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;player_order&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;start&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;lineno&quot;&gt;67 &lt;/span&gt;        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Rotate player order so that start goes first&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;68 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&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;lineno&quot;&gt;69 &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;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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;70 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;start_idx&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;names&lt;/span&gt;&lt;span class=&quot;o&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;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;71 &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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;72 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;73 &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;74 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Read player names from command line&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;75 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;player_names&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;lineno&quot;&gt;76 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Game&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;player_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;77 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let&amp;rsquo;s add types to this code.&lt;/p&gt;
&lt;h3 id=&quot;type-hints-for-methods&quot;&gt;Type Hints for Methods&lt;/h3&gt;
&lt;p&gt;First of all type hints for methods work much the same as type hints for functions. The only difference is that the &lt;code&gt;self&lt;/code&gt; argument need not be annotated, as it always will be a class instance. The types of the &lt;code&gt;Card&lt;/code&gt; class are easy to add:&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; 6 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Card&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;SUITS&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;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;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2 3 4 5 6 7 8 9 10 J Q K A&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;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;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;suit&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;rank&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;o&quot;&gt;-&amp;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;lineno&quot;&gt;11 &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;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &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;rank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &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;o&quot;&gt;-&amp;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;lineno&quot;&gt;15 &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;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.suit}{self.rank}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that the &lt;code&gt;.__init__()&lt;/code&gt; method always should have &lt;code&gt;None&lt;/code&gt; as its return type.&lt;/p&gt;
&lt;h3 id=&quot;classes-as-types&quot;&gt;Classes as Types&lt;/h3&gt;
&lt;p&gt;There is a correspondence between classes and types. For example, all instances of the &lt;code&gt;Card&lt;/code&gt; class together form the &lt;code&gt;Card&lt;/code&gt; type. To use classes as types you simply use the name of the class.&lt;/p&gt;
&lt;p&gt;For example, a &lt;code&gt;Deck&lt;/code&gt; essentially consists of a list of &lt;code&gt;Card&lt;/code&gt; objects. You can annotate this as follows:&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;17 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Deck&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;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;cards&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;Card&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;kc&quot;&gt;None&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Mypy is able to connect your use of &lt;code&gt;Card&lt;/code&gt; in the annotation with the definition of the &lt;code&gt;Card&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;This doesn&amp;rsquo;t work as cleanly though when you need to refer to the class currently being defined. For example, the &lt;code&gt;Deck.create()&lt;/code&gt; class method returns an object with type &lt;code&gt;Deck&lt;/code&gt;. However, you can&amp;rsquo;t simply add &lt;code&gt;-&amp;gt; Deck&lt;/code&gt; as the &lt;code&gt;Deck&lt;/code&gt; class is not yet fully defined.&lt;/p&gt;
&lt;p&gt;Instead, you are allowed to use string literals in annotations. These strings will only be evaluated by the type checker later, and can therefore contain self and forward references. The &lt;code&gt;.create()&lt;/code&gt; method should use such string literals for its types:&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;20 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Deck&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Create a new deck of 52 cards&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;cards&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;Card&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&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;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SUITS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &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;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that the &lt;code&gt;Player&lt;/code&gt; class also will reference the &lt;code&gt;Deck&lt;/code&gt; class. This is however no problem, since &lt;code&gt;Deck&lt;/code&gt; is defined before &lt;code&gt;Player&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;34 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35 &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&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;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36 &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&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;lineno&quot;&gt;37 &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;hand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Usually annotations are not used at runtime. This has given wings to the idea of &lt;a href=&quot;https://www.python.org/dev/peps/pep-0563/&quot;&gt;postponing the evaluation of annotations&lt;/a&gt;. Instead of evaluating annotations as Python expressions and storing their value, the proposal is to store the string representation of the annotation and only evaluate it when needed.&lt;/p&gt;
&lt;p&gt;Such functionality is planned to become standard in the still mythical &lt;a href=&quot;http://www.curiousefficiency.org/posts/2014/08/python-4000.html&quot;&gt;Python 4.0&lt;/a&gt;. However, in &lt;a href=&quot;https://realpython.com/python37-new-features/&quot;&gt;Python 3.7&lt;/a&gt; and later, forward references are available through a &lt;code&gt;__future__&lt;/code&gt; import:&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;__future__&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;annotations&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;With the &lt;code&gt;__future__&lt;/code&gt; import you can use &lt;code&gt;Deck&lt;/code&gt; instead of &lt;code&gt;&quot;Deck&quot;&lt;/code&gt; even before &lt;code&gt;Deck&lt;/code&gt; is defined.&lt;/p&gt;
&lt;h3 id=&quot;returning-self-or-cls&quot;&gt;Returning &lt;code&gt;self&lt;/code&gt; or &lt;code&gt;cls&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;As noted, you should typically not annotate the &lt;code&gt;self&lt;/code&gt; or &lt;code&gt;cls&lt;/code&gt; arguments. Partly, this is not necessary as &lt;code&gt;self&lt;/code&gt; points to an instance of the class, so it will have the type of the class. In the &lt;code&gt;Card&lt;/code&gt; example, &lt;code&gt;self&lt;/code&gt; has the implicit type &lt;code&gt;Card&lt;/code&gt;. Also, adding this type explicitly would be cumbersome since the class is not defined yet. You would have to use the string literal syntax, &lt;code&gt;self: &quot;Card&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is one case where you might want to annotate &lt;code&gt;self&lt;/code&gt; or &lt;code&gt;cls&lt;/code&gt;, though. Consider what happens if you have a superclass that other classes inherit from, and which has methods that return &lt;code&gt;self&lt;/code&gt; or &lt;code&gt;cls&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;c1&quot;&gt;# dogs.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &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;date&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Animal&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;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&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;birthday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&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;kc&quot;&gt;None&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;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&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;lineno&quot;&gt; 8 &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;birthday&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;birthday&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;nd&quot;&gt;@classmethod&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;newborn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&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;nb&quot;&gt;str&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;s2&quot;&gt;&amp;quot;Animal&amp;quot;&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&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;date&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;lineno&quot;&gt;13 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;twin&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;nb&quot;&gt;str&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;s2&quot;&gt;&amp;quot;Animal&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;bp&quot;&gt;cls&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;vm&quot;&gt;__class__&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&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;birthday&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;lineno&quot;&gt;18 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dog&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;lineno&quot;&gt;19 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bark&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;kc&quot;&gt;None&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;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;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; says woof!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newborn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Fido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pluto&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;twin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Pluto&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pluto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While the code runs without problems, Mypy will flag a 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;gp&quot;&gt;$&lt;/span&gt; mypy dogs.py
&lt;span class=&quot;go&quot;&gt;dogs.py:24: error: &amp;quot;Animal&amp;quot; has no attribute &amp;quot;bark&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dogs.py:25: error: &amp;quot;Animal&amp;quot; has no attribute &amp;quot;bark&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The issue is that even though the inherited &lt;code&gt;Dog.newborn()&lt;/code&gt; and &lt;code&gt;Dog.twin()&lt;/code&gt; methods will return a &lt;code&gt;Dog&lt;/code&gt; the annotation says that they return an &lt;code&gt;Animal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In cases like this you want to be more careful to make sure the annotation is correct. The return type should match the type of &lt;code&gt;self&lt;/code&gt; or the instance type of &lt;code&gt;cls&lt;/code&gt;. This can be done using type variables that keep track of what is actually passed to &lt;code&gt;self&lt;/code&gt; and &lt;code&gt;cls&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;c1&quot;&gt;# dogs.py&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;date&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;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;TAnimal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;TAnimal&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bound&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Animal&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Animal&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&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;birthday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&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;kc&quot;&gt;None&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&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;birthday&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;birthday&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newborn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TAnimal&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;nb&quot;&gt;str&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;TAnimal&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;cls&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;date&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;twin&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;TAnimal&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;nb&quot;&gt;str&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;TAnimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;cls&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;vm&quot;&gt;__class__&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&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;birthday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dog&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bark&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;kc&quot;&gt;None&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;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; says woof!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newborn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Fido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pluto&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;twin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Pluto&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pluto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are a few things to note in this example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The type variable &lt;code&gt;TAnimal&lt;/code&gt; is used to denote that return values might be instances of subclasses of &lt;code&gt;Animal&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We specify that &lt;code&gt;Animal&lt;/code&gt; is an upper bound for &lt;code&gt;TAnimal&lt;/code&gt;. Specifying &lt;code&gt;bound&lt;/code&gt;  means that &lt;code&gt;TAnimal&lt;/code&gt; will only be &lt;code&gt;Animal&lt;/code&gt; or one of its subclasses. This is needed to properly restrict the types that are allowed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;typing.Type[]&lt;/code&gt; construct is the typing equivalent of &lt;code&gt;type()&lt;/code&gt;. You need it to note that the class method expects a class and returns an instance of that class.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;annotating-args-and-kwargs&quot;&gt;Annotating &lt;code&gt;*args&lt;/code&gt; and &lt;code&gt;**kwargs&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;In the &lt;a href=&quot;#example-the-objective-of-the-game&quot;&gt;object oriented version&lt;/a&gt; of the game, we added the option to name the players on the command line. This is done by listing player names after the name of the program:&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 game.py GeirArne Dan Joanna
&lt;span class=&quot;go&quot;&gt;Dan: ♢A   Joanna: ♡9   P1: ♣A   GeirArne: ♣2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♡A   Joanna: ♡6   P1: ♠4   GeirArne: ♢8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♢K   Joanna: ♢Q   P1: ♣K   GeirArne: ♠5&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♡2   Joanna: ♡J   P1: ♠7   GeirArne: ♡K&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♢10  Joanna: ♣3   P1: ♢4   GeirArne: ♠8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♣6   Joanna: ♡Q   P1: ♣Q   GeirArne: ♢J&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♢2   Joanna: ♡4   P1: ♣8   GeirArne: ♡7&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♡10  Joanna: ♢3   P1: ♡3   GeirArne: ♠2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♠K   Joanna: ♣5   P1: ♣7   GeirArne: ♠J&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♠6   Joanna: ♢9   P1: ♣J   GeirArne: ♣10&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♠3   Joanna: ♡5   P1: ♣9   GeirArne: ♠Q&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♠A   Joanna: ♠9   P1: ♠10  GeirArne: ♡8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Dan: ♢6   Joanna: ♢5   P1: ♢7   GeirArne: ♣4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is implemented by unpacking and passing in &lt;code&gt;sys.argv&lt;/code&gt; to &lt;code&gt;Game()&lt;/code&gt; when it&amp;rsquo;s instantiated. The &lt;code&gt;.__init__()&lt;/code&gt; method uses &lt;code&gt;*names&lt;/code&gt; to pack the given names into a tuple.&lt;/p&gt;
&lt;p&gt;Regarding type annotations: even though &lt;code&gt;names&lt;/code&gt; will be a tuple of strings, you should only annotate the type of each name. In other words, you should use &lt;code&gt;str&lt;/code&gt; and not &lt;code&gt;Tuple[str]&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;46 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;47 &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;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;o&quot;&gt;-&amp;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;lineno&quot;&gt;48 &lt;/span&gt;        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Set up the deck and deal cards to 4 players&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;49 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&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;lineno&quot;&gt;50 &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;names&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;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;s2&quot;&gt;&amp;quot;P1 P2 P3 P4&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;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;51 &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;hands&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;lineno&quot;&gt;52 &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&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;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deal&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;lineno&quot;&gt;53 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Similarly, if you have a function or method accepting &lt;code&gt;**kwargs&lt;/code&gt; you should only annotate the type of each possible keyword argument.&lt;/p&gt;
&lt;h3 id=&quot;callables&quot;&gt;Callables&lt;/h3&gt;
&lt;p&gt;Functions are &lt;a href=&quot;https://dbader.org/blog/python-first-class-functions&quot;&gt;first-class objects&lt;/a&gt; in Python. This means that you can use functions as arguments to other functions. That also means that you need to be able to add type hints representing functions.&lt;/p&gt;
&lt;p&gt;Functions, as well as lambdas, methods and classes, are &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/kinds_of_types.html#callable-types-and-lambdas&quot;&gt;represented by &lt;code&gt;typing.Callable&lt;/code&gt;&lt;/a&gt;. The types of the arguments and the return value are usually also represented. For instance, &lt;code&gt;Callable[[A1, A2, A3], Rt]&lt;/code&gt; represents a function with three arguments with types &lt;code&gt;A1&lt;/code&gt;, &lt;code&gt;A2&lt;/code&gt;, and &lt;code&gt;A3&lt;/code&gt;, respectively. The return type of the function is &lt;code&gt;Rt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the following example, the function &lt;code&gt;do_twice()&lt;/code&gt; calls a given function twice and prints the return values:&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;c1&quot;&gt;# do_twice.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &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;Callable&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;do_twice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Callable&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;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argument&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;o&quot;&gt;-&amp;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;lineno&quot;&gt; 6 &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;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argument&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;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argument&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;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_greeting&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;nb&quot;&gt;str&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;nb&quot;&gt;str&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;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;Hello &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;do_twice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jekyll&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note the annotation of the  &lt;code&gt;func&lt;/code&gt; argument to &lt;code&gt;do_twice()&lt;/code&gt; on line 5. It says that &lt;code&gt;func&lt;/code&gt; should be a callable with one string argument, that also returns a string. One example of such a callable is &lt;code&gt;create_greeting()&lt;/code&gt; defined on line 9.&lt;/p&gt;
&lt;p&gt;Most callable types can be annotated in a similar manner. However, if you need more flexibility, check out &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/protocols.html#callback-protocols&quot;&gt;callback protocols&lt;/a&gt; and &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/additional_features.html#extended-callable-types&quot;&gt;extended callable types&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;example-hearts&quot;&gt;Example: Hearts&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s end with a full example of the game of &lt;a href=&quot;https://en.wikipedia.org/wiki/Hearts&quot;&gt;Hearts&lt;/a&gt;. You might already know this game from other computer simulations. Here is a quick recap of the rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Four players play with a hand of 13 cards each.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The player holding the ♣2 starts the first round, and must play ♣2.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Players take turns playing cards, following the leading suit if possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The player playing the highest card in the leading suit wins the trick, and becomes start player in the next turn.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A player can not lead with a ♡ until a ♡ has already been played in an earlier trick.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After all cards are played, players get points if they take certain cards:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;13 points for the ♠Q&lt;/li&gt;
&lt;li&gt;1 point for each ♡&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A game lasts several rounds, until one player has 100 points or more. The player with the least points wins.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More details can be found &lt;a href=&quot;https://www.bicyclecards.com/how-to-play/hearts&quot;&gt;found online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are not many new typing concepts in this example that you have not already seen. We&amp;rsquo;ll therefore not go through this code in detail, but leave it as an example of annotated code.&lt;/p&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_card311f38&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;#collapse311f38&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse311f38&quot;&gt;Source Code for the Hearts Card Game&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapse311f38&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse311f38&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapse311f38&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_card311f38&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;You can download this code and other examples from &lt;a href=&quot;https://github.com/realpython/materials/tree/master/python-type-checking&quot;&gt;GitHub&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;# hearts.py&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;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;sys&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;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&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;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Union&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;overload&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SUITS&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;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;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2 3 4 5 6 7 8 9 10 J Q K A&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;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;suit&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;rank&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;o&quot;&gt;-&amp;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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suit&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;rank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rank&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;value&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;nb&quot;&gt;int&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;The value of a card is rank as a number&amp;quot;&amp;quot;&amp;quot;&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;RANKS&lt;/span&gt;&lt;span class=&quot;o&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;&lt;span class=&quot;p&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;points&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;nb&quot;&gt;int&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;Points this card is worth&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&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;suit&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;ow&quot;&gt;and&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;rank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Q&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;mi&quot;&gt;13&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&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;suit&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;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&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;__eq__&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;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&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;Any&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;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&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;rank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__lt__&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;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&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;Any&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;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&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;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;o&quot;&gt;-&amp;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;return&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;{self.suit}{self.rank}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&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;cards&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;Card&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;kc&quot;&gt;None&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;cards&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Deck&amp;quot;&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;Create a new deck of 52 cards&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cards&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;Card&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&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;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SUITS&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;shuffle&lt;/span&gt;&lt;span class=&quot;p&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;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&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;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&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;play&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;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&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;kc&quot;&gt;None&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;Play one card by removing it from the deck&amp;quot;&amp;quot;&amp;quot;&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;cards&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&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;deal&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;num_hands&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Deck&amp;quot;&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Deal the cards in the deck into a number of hands&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tuple&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;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_hands&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;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;n&quot;&gt;num_hands&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;add_cards&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;cards&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;Card&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;kc&quot;&gt;None&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;Add a list of cards to the deck&amp;quot;&amp;quot;&amp;quot;&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;cards&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__len__&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;nb&quot;&gt;int&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;nb&quot;&gt;len&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;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@overload&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__getitem__&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;key&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&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;@overload&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__getitem__&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;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;slice&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;s2&quot;&gt;&amp;quot;Deck&amp;quot;&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;__getitem__&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;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Union&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;nb&quot;&gt;slice&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;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Deck&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;nb&quot;&gt;isinstance&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;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;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;cards&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;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&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;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;cls&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;vm&quot;&gt;__class__&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&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;cards&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;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;TypeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Indices must be integers or slices&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;__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;o&quot;&gt;-&amp;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;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&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;nb&quot;&gt;repr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&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;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Player&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&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;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deck&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;kc&quot;&gt;None&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;kc&quot;&gt;None&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&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;hand&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;playable_cards&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;played&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;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;List which cards in hand are playable this round&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&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;2&amp;quot;&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&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;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&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;2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;hand&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&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;non_winning_cards&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;played&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;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&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;Deck&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;List playable cards that are guaranteed to not win the trick&amp;quot;&amp;quot;&amp;quot;&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;played&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;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([])&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;best_card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&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;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_card&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&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;play_card&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;played&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;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&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;Play a card from a cpu player&amp;#39;s hand&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;playable&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;playable_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;non_winning&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;non_winning_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# Strategy&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;non_winning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Highest card not winning the trick, prefer points&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;non_winning&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;c&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;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&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;elif&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;played&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;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Lowest card maybe winning, avoid points&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;playable&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;c&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;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&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;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Highest card guaranteed winning, avoid points&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;playable&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;c&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;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&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;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -&amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{card}&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;has_card&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;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&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;nb&quot;&gt;bool&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;card&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;hand&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;o&quot;&gt;-&amp;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;return&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;{self.__class__.__name__}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name!r}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.hand}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&amp;quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HumanPlayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&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;play_card&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;played&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;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&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;Play a card from a human player&amp;#39;s hand&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;o&quot;&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;playable_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_str&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;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;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{c}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&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;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;np_str&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;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;nb&quot;&gt;repr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&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;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;hand&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&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;playable&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;s2&quot;&gt;&amp;quot;  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{p_str}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  (Rest: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{np_str}&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;k&quot;&gt;while&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;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;card_num&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;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;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, choose card: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&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;ne&quot;&gt;IndexError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&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;break&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;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&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;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; =&amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{card}&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HeartsGame&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;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;o&quot;&gt;-&amp;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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&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;s2&quot;&gt;&amp;quot;P1 P2 P3 P4&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;mi&quot;&gt;4&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;players&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;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&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;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;names&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&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;HumanPlayer&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;names&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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&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;kc&quot;&gt;None&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;Play a game of Hearts until one player go bust&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;score&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;n&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;all&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;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&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;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Starting new round:&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;round_score&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;play_round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;score&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;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;round_score&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;Scores:&amp;quot;&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;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_score&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&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;4&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;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name:&amp;lt;15}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{round_score[name]:&amp;gt;3}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{total_score:&amp;gt;3}&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;winners&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;n&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;names&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&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;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&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;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;#39; and &amp;#39;.join(winners)} won the game&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;play_round&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;Dict&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;nb&quot;&gt;int&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;Play a round of the Hearts card game&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&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;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deal&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;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;start_player&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;players&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has_card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&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;2&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;tricks&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;p&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;n&quot;&gt;Deck&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;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;hearts&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;c1&quot;&gt;# Play cards from each player&amp;#39;s hand until empty&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;played&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;Card&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;n&quot;&gt;turn_order&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;player_order&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;n&quot;&gt;start_player&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;player&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turn_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play_card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hearts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;played&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;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;start_player&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;trick_winner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turn_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tricks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_player&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&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;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{start_player.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; wins the trick&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;n&quot;&gt;hearts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&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;count_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tricks&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;player_order&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;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&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;kc&quot;&gt;None&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;Player&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;Rotate player order so that start goes first&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&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;start&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;choice&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;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;start_idx&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;players&lt;/span&gt;&lt;span class=&quot;o&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;start&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;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@staticmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trick_winner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trick&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;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;players&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;Player&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;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trick&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;valid&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&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;n&quot;&gt;p&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;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;players&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;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&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;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid&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;nd&quot;&gt;@staticmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;count_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tricks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&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;Deck&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;Dict&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;nb&quot;&gt;int&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&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;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&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;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tricks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&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;# Read player names from the command line&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;player_names&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;n&quot;&gt;game&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HeartsGame&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;player_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;/div&gt;
&lt;p&gt;Here are a few points to note in the code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For type relationships that are hard to express using &lt;code&gt;Union&lt;/code&gt; or type variables, you can use the &lt;code&gt;@overload&lt;/code&gt; decorator. See &lt;code&gt;Deck.__getitem__()&lt;/code&gt; for an example and &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/more_types.html#function-overloading&quot;&gt;the documentation&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Subclasses correspond to subtypes, so that a &lt;code&gt;HumanPlayer&lt;/code&gt; can be used wherever a &lt;code&gt;Player&lt;/code&gt; is expected.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When a subclass reimplements a method from a superclass, the type annotations must match. See &lt;code&gt;HumanPlayer.play_card()&lt;/code&gt; for an example.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When starting the game, you control the first player. Enter numbers to choose which cards to play. The following is an example of game play, with the highlighted lines showing where the player made a choice:&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 hearts.py GeirArne Aldren Joanna Brad

&lt;span class=&quot;go&quot;&gt;Starting new round:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Brad -&amp;gt; ♣2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  0: ♣5  1: ♣Q  2: ♣K  (Rest: ♢6 ♡10 ♡6 ♠J ♡3 ♡9 ♢10 ♠7 ♠K ♠4)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;  GeirArne, choose card: 2&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;GeirArne =&amp;gt; ♣K&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Aldren -&amp;gt; ♣10&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Joanna -&amp;gt; ♣9&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;GeirArne wins the trick&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  0: ♠4  1: ♣5  2: ♢6  3: ♠7  4: ♢10  5: ♠J  6: ♣Q  7: ♠K  (Rest: ♡10 ♡6 ♡3 ♡9)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;  GeirArne, choose card: 0&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;GeirArne =&amp;gt; ♠4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Aldren -&amp;gt; ♠5&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Joanna -&amp;gt; ♠3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Brad -&amp;gt; ♠2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Aldren wins the trick&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Joanna -&amp;gt; ♡J&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Brad -&amp;gt; ♡2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  0: ♡6  1: ♡9  (Rest: )&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;  GeirArne, choose card: 1&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;GeirArne =&amp;gt; ♡9&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Aldren -&amp;gt; ♡A&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Aldren wins the trick&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Aldren -&amp;gt; ♣A&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Joanna -&amp;gt; ♡Q&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Brad -&amp;gt; ♣J&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  0: ♡6  (Rest: )&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;  GeirArne, choose card: 0&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;GeirArne =&amp;gt; ♡6&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Aldren wins the trick&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Scores:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Brad             14  14&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Aldren           10  10&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;GeirArne          1   1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Joanna            1   1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;static-type-checking&quot;&gt;Static Type Checking&lt;/h2&gt;
&lt;p&gt;So far you have seen how to add type hints to your code. In this section you&amp;rsquo;ll learn more about how to actually perform static type checking of Python code.&lt;/p&gt;
&lt;h3 id=&quot;the-mypy-project&quot;&gt;The Mypy Project&lt;/h3&gt;
&lt;p&gt;Mypy was started by Jukka Lehtosalo during his Ph.D. studies at Cambridge around 2012. Mypy was originally envisioned as a Python variant with seamless dynamic and static typing. See &lt;a href=&quot;https://www.slideshare.net/jukkaleh/mypy-pyconfi2012&quot;&gt;Jukka&amp;rsquo;s slides from PyCon Finland 2012&lt;/a&gt; for examples of the original vision of Mypy.&lt;/p&gt;
&lt;p&gt;Most of those original ideas still play a big part in the Mypy project. In fact, the slogan &amp;ldquo;Seamless dynamic and static typing&amp;rdquo; is still &lt;a href=&quot;http://mypy-lang.org&quot;&gt;prominently visible on Mypy&amp;rsquo;s home page&lt;/a&gt; and describes the motivation for using type hints in Python well.&lt;/p&gt;
&lt;p&gt;The biggest change since 2012 is that Mypy is no longer a &lt;em&gt;variant&lt;/em&gt; of Python. In its first versions Mypy was a stand-alone language that was compatible with Python except for its type declarations. Following a &lt;a href=&quot;https://twitter.com/gvanrossum/status/700741601966985216&quot;&gt;suggestion by Guido van Rossum&lt;/a&gt;, Mypy was rewritten to use annotations instead. Today Mypy is a static type checker for &lt;em&gt;regular&lt;/em&gt; Python code.&lt;/p&gt;
&lt;h3 id=&quot;running-mypy&quot;&gt;Running Mypy&lt;/h3&gt;
&lt;p&gt;Before running Mypy for the first time, you must install the program. This is most easily done using &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 mypy
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With Mypy installed, you can run it as a regular command line program:&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; mypy my_program.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Running Mypy on your &lt;code&gt;my_program.py&lt;/code&gt; Python file will check it for type errors without actually executing the code.&lt;/p&gt;
&lt;p&gt;There are many available options when type checking your code. As Mypy is still under very active development, command line options are liable to change between versions. You should refer to Mypy&amp;rsquo;s help to see which settings are default on your 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; mypy --help
&lt;span class=&quot;go&quot;&gt;usage: mypy [-h] [-v] [-V] [more options; see below]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;            [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Mypy is a program that will type check your Python code.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;[... The rest of the help hidden for brevity ...]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Additionally, the &lt;a href=&quot;https://mypy.readthedocs.io/en/stable/command_line.html#command-line&quot;&gt;Mypy command line documentation online&lt;/a&gt; has a lot of information.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at some of the most common options. First of all, if you are using third-party packages without type hints, you may want to silence Mypy&amp;rsquo;s warnings about these. This can be done with the &lt;code&gt;--ignore-missing-imports&lt;/code&gt; option.&lt;/p&gt;
&lt;p&gt;The following example uses &lt;a href=&quot;https://realpython.com/numpy-array-programming/&quot;&gt;Numpy&lt;/a&gt; to calculate and print the cosine of several numbers:&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;c1&quot;&gt;# cosine.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;np&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;print_cosine&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;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ndarray&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;kc&quot;&gt;None&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;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printoptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;precision&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;n&quot;&gt;suppress&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;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;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cos&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;lineno&quot;&gt; 8 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &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;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linspace&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;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&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;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print_cosine&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;a href=&quot;https://docs.scipy.org/doc/numpy/reference/generated/numpy.printoptions.html&quot;&gt;&lt;code&gt;np.printoptions()&lt;/code&gt;&lt;/a&gt; is only available in version 1.15 and later of Numpy. Running this example prints some numbers to the 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; python cosine.py
&lt;span class=&quot;go&quot;&gt;[ 1.     0.707  0.    -0.707 -1.    -0.707 -0.     0.707  1.   ]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The actual output of this example is not important. However, you should note that the argument &lt;code&gt;x&lt;/code&gt; is annotated with &lt;code&gt;np.ndarray&lt;/code&gt; on line 5, as we want to print the cosine of a full array of numbers.&lt;/p&gt;
&lt;p&gt;You can run Mypy on this file as usual:&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; mypy cosine.py 
&lt;span class=&quot;go&quot;&gt;cosine.py:3: error: No library stub file for module &amp;#39;numpy&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;cosine.py:3: note: (Stub files are from https://github.com/python/typeshed)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These warnings may not immediately make much sense to you, but you&amp;rsquo;ll learn about &lt;a href=&quot;#adding-stubs&quot;&gt;stubs&lt;/a&gt; and &lt;a href=&quot;#typeshed&quot;&gt;typeshed&lt;/a&gt; soon. You can essentially read the warnings as Mypy saying that the Numpy package does not contain type hints.&lt;/p&gt;
&lt;p&gt;In most cases, missing type hints in third-party packages is not something you want to be bothered with so you can silence these messages:&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; mypy --ignore-missing-imports cosine.py 
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you use the &lt;code&gt;--ignore-missing-import&lt;/code&gt; command line option, Mypy will &lt;a href=&quot;https://mypy.readthedocs.io/en/stable/running_mypy.html#how-mypy-handles-imports&quot;&gt;not try to follow or warn about any missing imports&lt;/a&gt;. This might be a bit heavy-handed though, as it also ignores actual mistakes, like misspelling the name of a package.&lt;/p&gt;
&lt;p&gt;Two less intrusive ways of handling third-party packages are using type comments or configuration files.&lt;/p&gt;
&lt;p&gt;In a simple example as the one above, you can silence the &lt;code&gt;numpy&lt;/code&gt; warning by adding a type comment to the line containing the import:&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; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;np&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# type: ignore&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The literal &lt;code&gt;# type: ignore&lt;/code&gt; tells Mypy to ignore the import of Numpy.&lt;/p&gt;
&lt;p&gt;If you have several files, it might be easier to keep track of which imports to ignore in a configuration file. Mypy reads a file called &lt;code&gt;mypy.ini&lt;/code&gt; in the current directory if it is present. This configuration file must contain a section called &lt;code&gt;[mypy]&lt;/code&gt; and may contain module specific sections of the form &lt;code&gt;[mypy-module]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The following configuration file will ignore that Numpy is missing type hints:&lt;/p&gt;
&lt;div class=&quot;highlight ini&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# mypy.ini&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[mypy]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[mypy-numpy]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;ignore_missing_imports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are many options that can be specified in the configuration file. It is also possible to specify a global configuration file. See the &lt;a href=&quot;https://mypy.readthedocs.io/en/stable/config_file.html&quot;&gt;documentation&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h3 id=&quot;adding-stubs&quot;&gt;Adding Stubs&lt;/h3&gt;
&lt;p&gt;Type hints are available for all the packages in the Python standard library. However, if you are using third-party packages you&amp;rsquo;ve already seen that the situation can be different.&lt;/p&gt;
&lt;p&gt;The following example uses the &lt;a href=&quot;https://pypi.org/project/parse/&quot;&gt;Parse package&lt;/a&gt; to do simple text parsing. To follow along you should first install Parse:&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 parse
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Parse can be used to recognize simple patterns. Here is a small program that tries its best to figure out your name:&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;c1&quot;&gt;# parse_name.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;parse&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;parse_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&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;o&quot;&gt;-&amp;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;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;patterns&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;lineno&quot;&gt; 7 &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;&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;s2&quot;&gt;&amp;quot;i&amp;#39;m &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&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; 9 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;i am &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&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;10 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;call me &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&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;11 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&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;12 &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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patterns&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;result&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;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;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;name&amp;quot;&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer&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;What is your name? &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;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &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;Hi &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, nice to meet you!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The main flow is defined in the last three lines: ask for your name, parse the answer, and print a greeting. The &lt;code&gt;parse&lt;/code&gt; package is called on line 14 in order to try to find a name based on one of the patterns listed on lines 7-11.&lt;/p&gt;
&lt;p&gt;The program can be used as follows:&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 parse_name.py
&lt;span class=&quot;go&quot;&gt;What is your name? I am Geir Arne&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Hi Geir Arne, nice to meet you!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that even though I answer &lt;code&gt;I am Geir Arne&lt;/code&gt;, the program figures out that &lt;code&gt;I am&lt;/code&gt; is not part of my name.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s add a small bug to the program, and see if Mypy is able to help us detect it. Change line 16 from &lt;code&gt;return result[&quot;name&quot;]&lt;/code&gt; to &lt;code&gt;return result&lt;/code&gt;. This will return a &lt;code&gt;parse.Result&lt;/code&gt; object instead of the string containing the name.&lt;/p&gt;
&lt;p&gt;Next run Mypy on the program:&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; mypy parse_name.py 
&lt;span class=&quot;go&quot;&gt;parse_name.py:3: error: Cannot find module named &amp;#39;parse&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;parse_name.py:3: note: (Perhaps setting MYPYPATH or using the&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                       &amp;quot;--ignore-missing-imports&amp;quot; flag would help)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Mypy prints a similar error to the one you saw in the previous section: It doesn&amp;rsquo;t know about the &lt;code&gt;parse&lt;/code&gt; package. You could try to ignore the import:&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; mypy parse_name.py --ignore-missing-imports
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately, ignoring the import means that Mypy has no way of discovering the bug in our program. A better solution would be to add type hints to the Parse package itself. As &lt;a href=&quot;https://github.com/r1chardj0n3s/parse&quot;&gt;Parse is open source&lt;/a&gt; you can actually add types to the source code and send a pull request.&lt;/p&gt;
&lt;p&gt;Alternatively, you can add the types in a &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/stubs.html&quot;&gt;stub file&lt;/a&gt;. A stub file is a text file that contains the signatures of methods and functions, but not their implementations. Their main function is to add type hints to code that you for some reason can&amp;rsquo;t change. To show how this works, we will add some stubs for the Parse package.&lt;/p&gt;
&lt;p&gt;First of all, you should put all your stub files inside one common directory, and set the &lt;code&gt;MYPYPATH&lt;/code&gt; environment variable to point to this directory. On Mac and Linux you can set &lt;code&gt;MYPYPATH&lt;/code&gt; as follows:&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;export&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;MYPYPATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/home/gahjelle/python/stubs
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can set the variable permanently by adding the line to your &lt;code&gt;.bashrc&lt;/code&gt; file. On Windows you can click the start menu and search for &lt;em&gt;environment variables&lt;/em&gt; to set &lt;code&gt;MYPYPATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, create a file inside your stubs directory that you call &lt;code&gt;parse.pyi&lt;/code&gt;. It must be named for the package that you are adding type hints for, with a &lt;code&gt;.pyi&lt;/code&gt; suffix. Leave this file empty for now. Then run Mypy again:&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; mypy parse_name.py
&lt;span class=&quot;go&quot;&gt;parse_name.py:14: error: Module has no attribute &amp;quot;parse&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you have set everything up correctly, you should see this new error message. Mypy uses the new &lt;code&gt;parse.pyi&lt;/code&gt; file to figure out which functions are available in the &lt;code&gt;parse&lt;/code&gt; package. Since the stub file is empty, Mypy assumes that &lt;code&gt;parse.parse()&lt;/code&gt; does not exist, and then gives the error you see above.&lt;/p&gt;
&lt;p&gt;The following example does not add types for the whole &lt;code&gt;parse&lt;/code&gt; package. Instead it shows the type hints you need to add in order for Mypy to type check your use of &lt;code&gt;parse.parse()&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;c1&quot;&gt;# parse.pyi&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;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Union&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Result&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;fixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&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;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mapping&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;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;spans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mapping&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;Tuple&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;nb&quot;&gt;int&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;o&quot;&gt;-&amp;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;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__getitem__&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;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Union&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;nb&quot;&gt;str&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;nb&quot;&gt;str&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;__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;o&quot;&gt;-&amp;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;o&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse&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;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;string&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;evaluate_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&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;n&quot;&gt;case_sensitive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&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;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;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&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;The ellipsis &lt;code&gt;...&lt;/code&gt; are part of the file, and should be written exactly as above. The stub file should only contain type hints for variables, attributes, functions, and methods, so the implementations should be left out and replaced by &lt;code&gt;...&lt;/code&gt; markers.&lt;/p&gt;
&lt;p&gt;Finally Mypy is able to spot the bug we introduced:&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; mypy parse_name.py
&lt;span class=&quot;go&quot;&gt;parse_name.py:16: error: Incompatible return value type (got&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                         &amp;quot;Result&amp;quot;, expected &amp;quot;str&amp;quot;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This points straight to line 16 and the fact that we return a &lt;code&gt;Result&lt;/code&gt; object and not the name string. Change &lt;code&gt;return result&lt;/code&gt; back to &lt;code&gt;return result[&quot;name&quot;]&lt;/code&gt;, and run Mypy again to see that it&amp;rsquo;s happy.&lt;/p&gt;
&lt;h3 id=&quot;typeshed&quot;&gt;Typeshed&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ve seen how to use stubs to add type hints without changing the source code itself. In the previous section we added some type hints to the third-party Parse package. Now, it wouldn&amp;rsquo;t be very effective if everybody needs to create their own stubs files for all third-party packages they are using.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/python/typeshed&quot;&gt;Typeshed&lt;/a&gt; is a Github repository that contains type hints for the Python standard library, as well as many third-party packages. Typeshed comes included with Mypy so if you are using a package that already has type hints defined in Typeshed, the type checking will just work.&lt;/p&gt;
&lt;p&gt;You can also &lt;a href=&quot;https://github.com/python/typeshed/blob/master/CONTRIBUTING.md&quot;&gt;contribute type hints to Typeshed&lt;/a&gt;. Make sure to get the permission of the owner of the package first though, especially because they might be working on adding type hints into the source code itself&amp;mdash;which is the &lt;a href=&quot;https://github.com/python/typeshed/blob/master/CONTRIBUTING.md#adding-a-new-library&quot;&gt;preferred approach&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;other-static-type-checkers&quot;&gt;Other Static Type Checkers&lt;/h3&gt;
&lt;p&gt;In this tutorial, we have mainly focused on type checking using Mypy. However, there are other static type checkers in the Python ecosystem.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://realpython.com/python-ides-code-editors-guide/#pycharm&quot;&gt;&lt;strong&gt;PyCharm&lt;/strong&gt;&lt;/a&gt; editor comes with its own type checker included. If you are using PyCharm to write your Python code, it will be automatically type checked.&lt;/p&gt;
&lt;p&gt;Facebook has developed &lt;a href=&quot;https://pyre-check.org/&quot;&gt;&lt;strong&gt;Pyre&lt;/strong&gt;&lt;/a&gt;. One of its stated goals is to be fast and performant. While there are some differences, Pyre functions mostly similar to Mypy. See the &lt;a href=&quot;https://pyre-check.org/docs/overview.html&quot;&gt;documentation&lt;/a&gt; if you&amp;rsquo;re interested in trying out Pyre.&lt;/p&gt;
&lt;p&gt;Furthermore, Google has created &lt;a href=&quot;https://github.com/google/pytype&quot;&gt;&lt;strong&gt;Pytype&lt;/strong&gt;&lt;/a&gt;. This type checker also works mostly the same as Mypy. In addition to checking annotated code, Pytype has some support for running type checks on unannotated code and even adding annotations to code automatically. See the &lt;a href=&quot;https://github.com/google/pytype/blob/master/docs/quickstart.md&quot;&gt;quickstart&lt;/a&gt; document for more information.&lt;/p&gt;
&lt;h3 id=&quot;using-types-at-runtime&quot;&gt;Using Types at Runtime&lt;/h3&gt;
&lt;p&gt;As a final note, it&amp;rsquo;s possible to use type hints also at runtime during execution of your Python program. Runtime type checking will probably never be natively supported in Python.&lt;/p&gt;
&lt;p&gt;However, the type hints are available at runtime in the &lt;code&gt;__annotations__&lt;/code&gt; dictionary, and you can use those to do type checks if you desire. Before you run off and write your own package for enforcing types, you should know that there are already several packages doing this for you. Have a look at &lt;a href=&quot;https://pypi.org/project/enforce/&quot;&gt;&lt;strong&gt;Enforce&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://pypi.org/project/pydantic/&quot;&gt;&lt;strong&gt;Pydantic&lt;/strong&gt;&lt;/a&gt;, or &lt;a href=&quot;https://pypi.org/project/pytypes/&quot;&gt;&lt;strong&gt;Pytypes&lt;/strong&gt;&lt;/a&gt; for some examples.&lt;/p&gt;
&lt;p&gt;Another use of type hints is for translating your Python code to C and compiling it for optimization. The popular &lt;a href=&quot;https://cython.org/&quot;&gt;&lt;strong&gt;Cython&lt;/strong&gt; project&lt;/a&gt; uses a hybrid C/Python language to write statically typed Python code. However, since version 0.27 Cython has also supported type annotations. Recently, the &lt;a href=&quot;https://github.com/mypyc/mypyc&quot;&gt;&lt;strong&gt;Mypyc&lt;/strong&gt; project&lt;/a&gt; has become available. While not yet ready for general use, it can compile some type annotated Python code to C extensions.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Type hinting in Python is a very useful feature that you can happily live without. Type hints don&amp;rsquo;t make you capable of writing any code you can&amp;rsquo;t write without using type hints. Instead, using type hints makes it easier for you to reason about code, find subtle bugs, and maintain a clean architecture.&lt;/p&gt;
&lt;p&gt;In this tutorial you have learned how type hinting works in Python, and how gradual typing make type checks in Python more flexible than in many other languages. You&amp;rsquo;ve seen some of the pros and cons of using type hints, and how they can be added to code using annotations or type comments. Finally you saw many of the different types that Python supports, as well as how to perform static type checking.&lt;/p&gt;
&lt;p&gt;There are many resources to learn more about static type checking in Python. &lt;a href=&quot;https://www.python.org/dev/peps/pep-0483/&quot;&gt;PEP 483&lt;/a&gt; and &lt;a href=&quot;https://www.python.org/dev/peps/pep-0484/&quot;&gt;PEP 484&lt;/a&gt; give a lot of background about how type checking is implemented in Python. The &lt;a href=&quot;https://mypy.readthedocs.io/&quot;&gt;Mypy documentation&lt;/a&gt; has a great &lt;a href=&quot;https://mypy.readthedocs.io/en/stable/builtin_types.html&quot;&gt;reference section&lt;/a&gt; detailing all the different types available.&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>Modeling Polymorphism in Django With Python</title>
      <id>https://realpython.com/modeling-polymorphism-django-python/</id>
      <link href="https://realpython.com/modeling-polymorphism-django-python/"/>
      <updated>2019-01-02T14:00:00+00:00</updated>
      <summary>Modeling polymorphism in relational databases can be a challenging task, but in this article, you&#39;ll learn several modeling techniques to represent polymorphic objects in a relational database using the Django object-relational mapping (ORM).</summary>
      <content type="html">
        &lt;p&gt;Modeling polymorphism in relational &lt;a href=&quot;https://realpython.com/search?q=database&quot;&gt;databases&lt;/a&gt; is a challenging task. In this article, we present several modeling techniques to represent polymorphic objects in a relational database using the Django object-relational mapping (&lt;a href=&quot;https://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORM&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This intermediate-level tutorial is designed for readers who are already familiar with the fundamental design of &lt;a href=&quot;https://realpython.com/tutorials/django/&quot;&gt;Django&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-django-resources-experiment&quot; data-focus=&quot;false&quot;&gt;Click here to get the most popular Django tutorials and resources on Real Python&lt;/a&gt; and improve your Django + Python web development skills.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;what-is-polymorphism&quot;&gt;What Is Polymorphism?&lt;/h2&gt;
&lt;p&gt;Polymorphism is the ability of an object to take on many forms. Common examples of polymorphic objects include event streams, different types of users, and products in an e-commerce website. A polymorphic model is used when a single entity requires different functionality or information.&lt;/p&gt;
&lt;p&gt;In the examples above, all events are logged for future use, but they can contain different data. All users need be able to log in, but they might have different profile structures. In every e-commerce website, a user wants to put different products in their shopping cart.&lt;/p&gt;
&lt;h2 id=&quot;why-is-modeling-polymorphism-challenging&quot;&gt;Why Is Modeling Polymorphism Challenging?&lt;/h2&gt;
&lt;p&gt;There are many ways to model polymorphism. Some approaches use standard features of the Django ORM, and some use special features of the Django ORM. The main challenges you&amp;rsquo;ll encounter when modeling polymorphic objects are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How to represent a single polymorphic object:&lt;/strong&gt; Polymorphic objects have different attributes. The Django ORM maps attributes to columns in the database. In that case, how should the Django ORM map attributes to the columns in the table? Should different objects reside in the same table? Should you have multiple tables?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How to reference instances of a polymorphic model:&lt;/strong&gt; To utilize database and Django ORM features, you need to reference objects using foreign keys. How you decide to represent a single polymorphic object is crucial to your ability to reference it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To truly understand the challenges of modeling polymorphism, you are going to take a small bookstore from its first online website to a big online shop selling all sorts of products. Along the way, you&amp;rsquo;ll experience and analyze different approaches for modeling polymorphism using the Django ORM.&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; To follow this tutorial, it is recommended that you use a PostgreSQL backend, Django 2.x, and Python 3.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to follow along with other database backends as well. In places where features unique to PostgreSQL are used, an alternative will be presented for other databases.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;naive-implementation&quot;&gt;Naive Implementation&lt;/h2&gt;
&lt;p&gt;You have a bookstore in a nice part of town right next to a coffee shop, and you want to start selling books online.&lt;/p&gt;
&lt;p&gt;You sell only one type of product: books. In your online store, you want to show details about the books, like name and price. You want your users to browse around the website and collect many books, so you also need a cart. You eventually need to ship the books to the user, so you need to know the weight of each book to calculate the delivery fee.&lt;/p&gt;
&lt;p&gt;Let’s create a simple model for your new book store:&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;django.contrib.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&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;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&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;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&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;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in grams&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;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;o&quot;&gt;-&amp;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;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;name&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&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;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&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;books&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To create a new book, you provide a name, price, and weight:&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;naive.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&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;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;weight&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Product: Python Tricks&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To create a cart, you first need to associate it with a user:&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;django.contrib.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&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;haki&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&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;create_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;haki&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;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;naive.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&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;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;haki&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then the user can start adding items to 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;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;products&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;book&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;products&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;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: Python Tricks&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Pro&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Easy to understand and maintain:&lt;/strong&gt; It&amp;rsquo;s sufficient for a single type of product.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Con&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Restricted to homogeneous products:&lt;/strong&gt; It only supports products with the same set of attributes. Polymorphism is not captured or permitted at all.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sparse-model&quot;&gt;Sparse Model&lt;/h2&gt;
&lt;p&gt;With the success of your online bookstore, users started to ask if you also sell e-books. E-books are a great product for your online store, and you want to start selling them right away.&lt;/p&gt;
&lt;p&gt;A physical book is different from an e-book:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;An e-book has no weight.  It’s a virtual product.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An e-book does not require shipment. Users download it from the website.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To make your existing model support the additional information for selling e-books, you add some fields to the existing &lt;code&gt;Book&lt;/code&gt; model:&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;django.contrib.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;physical&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;virtual&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_CHOICES&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Physical&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;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Virtual&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;hll&quot;&gt;    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;choices&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_CHOICES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Common attributes&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;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&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;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&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;# Specific attributes&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in grams&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;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URLField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;null&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;blank&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&gt;&lt;span class=&quot;hll&quot;&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;__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;o&quot;&gt;-&amp;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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;[{self.get_type_display()}] &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&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;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&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;books&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Book&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;First, you added a type field to indicate what type of book it is. Then, you added a URL field to store the download link of the e-book.&lt;/p&gt;
&lt;p&gt;To add a physical book to your bookstore, do the following:&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;sparse.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&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;physical_book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&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;Python Tricks&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;price&lt;/span&gt;&lt;span class=&quot;o&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;weight&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;download_link&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;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;n&quot;&gt;physical_book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: [Physical] Python Tricks&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To add a new e-book, you do the following:&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;virtual_book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&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 Old Man and the Sea&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;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&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;weight&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;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://books.com/12345&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;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;virtual_book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: [Virtual] The Old Man and the Sea&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your users can now add both books and e-books to the cart:&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;sparse.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&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;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&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;physical_book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;virtual_book&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&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;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: [Physical] Python Tricks&amp;gt;, &amp;lt;Book: [Virtual] The Old Man and the Sea&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The virtual books are a big hit, and you decide to hire employees. The new employees are apparently not so tech savvy, and you start seeing weird things in the database:&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;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&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;Python Tricks&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;price&lt;/span&gt;&lt;span class=&quot;o&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;weight&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;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://books.com/54321&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;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That book apparently weighs &lt;code&gt;0&lt;/code&gt; pounds and has a download link.&lt;/p&gt;
&lt;p&gt;This e-book apparently weighs 100g and has no download link:&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;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&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;Python Tricks&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;price&lt;/span&gt;&lt;span class=&quot;o&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;weight&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;download_link&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;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This doesn&amp;rsquo;t make any sense. You have a data integrity problem.&lt;/p&gt;
&lt;p&gt;To overcome integrity problems, you add validations to the model:&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;django.core.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clean&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;kc&quot;&gt;None&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&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;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;A virtual product weight cannot exceed zero.&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&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;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;A virtual product must have a download link.&amp;#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;elif&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;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&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;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;A physical product weight must exceed zero.&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_link&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;kc&quot;&gt;None&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;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;A physical product cannot have a download link.&amp;#39;&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;assert&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;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Unknown product type &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.type}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;quot;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You used &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/models/instances/#django.db.models.Model.clean&quot;&gt;Django’s built-in validation mechanism&lt;/a&gt; to enforce data integrity rules. &lt;code&gt;clean()&lt;/code&gt; is only called automatically by Django forms. For objects that are not created by a Django form, you need to make sure to explicitly validate the object.&lt;/p&gt;
&lt;p&gt;To keep the integrity of the &lt;code&gt;Book&lt;/code&gt; model intact, you need to make a little change to the way you create books:&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;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&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;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&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;Python Tricks&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;price&lt;/span&gt;&lt;span class=&quot;o&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;weight&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;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://books.com/54321&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;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_clean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;ValidationError: {&amp;#39;__all__&amp;#39;: [&amp;#39;A physical product weight must exceed zero.&amp;#39;]}&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;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&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;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&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;Python Tricks&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;price&lt;/span&gt;&lt;span class=&quot;o&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;weight&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;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;download_link&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;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_clean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;ValidationError: {&amp;#39;__all__&amp;#39;: [&amp;#39;A virtual product weight cannot exceed zero.&amp;#39;]}&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When creating objects using the default manager (&lt;code&gt;Book.objects.create(...)&lt;/code&gt;), Django will create an object and immediately persist it to the database.&lt;/p&gt;
&lt;p&gt;In your case, you want to validate the object before saving if to the database. You first create the object (&lt;code&gt;Book(...)&lt;/code&gt;), validate it (&lt;code&gt;book.full_clean()&lt;/code&gt;), and only then save it (&lt;code&gt;book.save()&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;Denormalization:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A sparse model is a product of &lt;a href=&quot;https://en.wikipedia.org/wiki/Denormalization&quot;&gt;denormalization&lt;/a&gt;. In a denormalization process, you inline attributes from multiple normalized models into a single table for better performance. A denormalized table will usually have a lot of nullable columns.&lt;/p&gt;
&lt;p&gt;Denormalizing is often used in decision support systems such as data warehouses where read performance is most important. Unlike &lt;a href=&quot;https://en.wikipedia.org/wiki/Online_transaction_processing&quot;&gt;OLTP systems&lt;/a&gt;, data warehouses are usually not required to enforce data integrity rules, which makes denormalization ideal.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Pro&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Easy to understand and maintain:&lt;/strong&gt; The sparse model is usually the first step we take when certain types of objects need more information. It&amp;rsquo;s very intuitive and easy to understand.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unable to utilize NOT NULL database constraints:&lt;/strong&gt; Null values are used for attributes that are not defined for all types of objects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complex validation logic:&lt;/strong&gt; Complex validation logic is required to enforce data integrity rules. The complex logic also requires more tests.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Many Null fields create clutter:&lt;/strong&gt; Representing multiple types of products in a single model makes it harder to understand and maintain.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;New types require schema changes:&lt;/strong&gt; New types of products require additional fields and validations.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The sparse model is ideal when you&amp;rsquo;re representing heterogeneous objects that share most attributes, and when new items are not added very often.&lt;/p&gt;
&lt;h2 id=&quot;semi-structured-model&quot;&gt;Semi-Structured Model&lt;/h2&gt;
&lt;p&gt;Your bookstore is now a huge success, and you are selling more and more books. You have books from different genres and publishers, e-books with different formats, books with odd shapes and sizes, and so on.&lt;/p&gt;
&lt;p&gt;In the sparse model approach, you added fields for every new type of product. The model now has a lot of nullable fields, and new developers and employees are having trouble keeping up.&lt;/p&gt;
&lt;p&gt;To address the clutter, you decide to keep only the common fields (&lt;code&gt;name&lt;/code&gt; and &lt;code&gt;price&lt;/code&gt;) on the model. You store the rest of the fields in a single &lt;code&gt;JSONField&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;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.postgres.fields&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JSONField&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;physical&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;virtual&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_CHOICES&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Physical&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;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Virtual&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;nb&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;choices&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_CHOICES&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;# Common attributes&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;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&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;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&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;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JSONField&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;__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;o&quot;&gt;-&amp;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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;[{self.get_type_display()}] &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&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;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&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;books&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;related_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;+&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;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;JSONField:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this example, you use PostgreSQL as a database backend. Django provides a built-in JSON field for PostgreSQL in &lt;code&gt;django.contrib.postgres.fields&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For other databases, such as SQLite and MySQL, there are &lt;a href=&quot;https://djangopackages.org/grids/g/json-fields/&quot;&gt;packages&lt;/a&gt; that provide similar functionality.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Your &lt;code&gt;Book&lt;/code&gt; model is now clutter-free. Common attributes are modeled as fields. Attributes that are not common to all types of products are stored in the &lt;code&gt;extra&lt;/code&gt; JSON field:&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;semi_structured.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&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;physical_book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&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;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&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;Python Tricks&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;price&lt;/span&gt;&lt;span class=&quot;o&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;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;extra&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;weight&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&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&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;n&quot;&gt;physical_book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_clean&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;physical_book&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;go&quot;&gt;&amp;lt;Book: [Physical] Python Tricks&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;virtual_book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&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;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&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 Old Man and the Sea&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;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;extra&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;download_link&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http://books.com/12345&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&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;n&quot;&gt;virtual_book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_clean&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;virtual_book&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;go&quot;&gt;&amp;lt;Book: [Virtual] The Old Man and the Sea&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;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;semi_structured.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&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;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&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;physical_book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;virtual_book&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&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;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: [Physical] Python Tricks&amp;gt;, &amp;lt;Book: [Virtual] The Old Man and the Sea&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Clearing up the clutter is important, but it comes with a cost. The validation logic is a lot more complicated:&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;django.core.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.core.validators&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;URLValidator&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clean&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;kc&quot;&gt;None&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;weight&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;weight&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&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;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;Weight must be a number&amp;#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;KeyError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&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;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&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;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;s1&quot;&gt;&amp;#39;A virtual product weight cannot exceed zero.&amp;#39;&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;download_link&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;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;download_link&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;KeyError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&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;c1&quot;&gt;# Will raise a validation error&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;URLValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;elif&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;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;weight&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;weight&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&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;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;Weight must be a number&amp;#39;&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;KeyError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&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;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&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;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;s1&quot;&gt;&amp;#39;A physical product weight must exceed zero.&amp;#39;&lt;/span&gt;
                     &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;download_link&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;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;download_link&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;KeyError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&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;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;download_link&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;kc&quot;&gt;None&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;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;s1&quot;&gt;&amp;#39;A physical product cannot have a download link.&amp;#39;&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;n&quot;&gt;ValidationError&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;Unknown product type &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.type}&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;The benefit of using a proper field is that it validates the type. Both Django and the Django ORM can perform checks to make sure the right type is used for the field. When using a &lt;code&gt;JSONField&lt;/code&gt;, you need to validate both the type and the value:&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;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&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;Python Tricks&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;price&lt;/span&gt;&lt;span class=&quot;o&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;extra&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;weight&amp;#39;&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;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;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_clean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ValidationError: {&amp;#39;__all__&amp;#39;: [&amp;#39;A virtual product weight cannot exceed zero.&amp;#39;]}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another issue with using JSON is that not all databases have proper support for querying and indexing values in JSON fields.&lt;/p&gt;
&lt;p&gt;In PostgreSQL for example, you can query all the books that weigh more than &lt;code&gt;100&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;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra__weight__gt&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;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: [Physical] Python Tricks&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, not all database vendors support that.&lt;/p&gt;
&lt;p&gt;Another restriction imposed when using JSON is that you are unable to use database constraints such as not null, unique, and foreign keys. You will have to implement these constraints in the application.&lt;/p&gt;
&lt;p&gt;This semi-structured approach resembles &lt;a href=&quot;https://en.wikipedia.org/wiki/NoSQL&quot;&gt;NoSQL&lt;/a&gt; architecture and has many of its advantages and disadvantages. The JSON field is a way to get around the strict schema of a relational database. This hybrid approach provides us with the flexibility to squash many object types into a single table while still maintaining some of the benefits of a relational, strictly and strongly typed database. For many common NoSQL use cases, this approach might actually be more suitable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduce clutter:&lt;/strong&gt; Common fields are stored on the model. Other fields are stored in a single JSON field.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Easier to add new types:&lt;/strong&gt; New types of products don&amp;rsquo;t require schema changes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complicated and ad hoc validation logic&lt;/strong&gt;: Validating a JSON field requires validating types as well as values. This challenge can be addressed by using other solutions to validate JSON data such as &lt;a href=&quot;https://json-schema.org/&quot;&gt;JSON schema&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unable to utilize database constraints&lt;/strong&gt;: Database constraints such as null null, unique and foreign key constraints, which enforce type and data integrity at the database level, cannot be used.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Restricted by database support for JSON&lt;/strong&gt;: Not all database vendors support querying and indexing JSON fields.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Schema is not enforced by the database system&lt;/strong&gt;: Schema changes might require backward compatibility or ad hoc migrations. Data can &amp;ldquo;rot.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No deep integration with the database metadata system&lt;/strong&gt;: Metadata about the fields is not stored in the database. Schema is only enforced at the application level.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A semi-structured model is ideal when you need to represent heterogeneous objects that don&amp;rsquo;t share many common attributes, and when new items are added often.&lt;/p&gt;
&lt;p&gt;A classic use case for the semi-structured approach is storing events (like logs, analytics, and event stores). Most events have a timestamp, type and metadata like device, user agent, user, and so on. The data for each type is stored in a JSON field. For analytics and log events, it&amp;rsquo;s important to be able to add new types of events with minimal effort, so this approach is ideal.&lt;/p&gt;
&lt;h2 id=&quot;abstract-base-model&quot;&gt;Abstract Base Model&lt;/h2&gt;
&lt;p&gt;So far, you&amp;rsquo;ve worked around the problem of actually treating your products as heterogeneous. You worked under the assumption that the differences between the products is minimal, so it made sense to maintain them in the same model. This assumption can take you only so far.&lt;/p&gt;
&lt;p&gt;Your little store is growing fast, and you want to start selling entirely different types of products, such as e-readers, pens, and notebooks.&lt;/p&gt;
&lt;p&gt;A book and an e-book are both products. A product is defined using common attributes such as name and price. In an object-oriented environment, you could look at a &lt;code&gt;Product&lt;/code&gt; as a base class or an interface. Every new type of product you add must implement the &lt;code&gt;Product&lt;/code&gt; class and extend it with its own attributes.&lt;/p&gt;
&lt;p&gt;Django offers the ability to create &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/topics/db/models/#abstract-base-classes&quot;&gt;abstract base classes&lt;/a&gt;. Let’s define a &lt;code&gt;Product&lt;/code&gt; abstract base class and add two models for &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;EBook&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;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;abstract&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&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;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&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;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&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;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;o&quot;&gt;-&amp;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;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;name&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in grams&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EBook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URLField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice that both &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;EBook&lt;/code&gt; inherit from &lt;code&gt;Product&lt;/code&gt;. The fields defined in the base class &lt;code&gt;Product&lt;/code&gt; are inherited, so the derived models &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;Ebook&lt;/code&gt; don&amp;rsquo;t need to repeat them.&lt;/p&gt;
&lt;p&gt;To add new products, you use the derived classes:&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;abstract_base_model.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&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;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;weight&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: Python Tricks&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;ebook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EBook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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 Old Man and the Sea&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;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&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;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://books.com/12345&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;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;ebook&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: The Old Man and the Sea&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You might have noticed that the &lt;code&gt;Cart&lt;/code&gt; model is missing. You can try to create a &lt;code&gt;Cart&lt;/code&gt; model with a &lt;code&gt;ManyToMany&lt;/code&gt; field to &lt;code&gt;Product&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;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;primary_key&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;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&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;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you try to reference a &lt;code&gt;ManyToMany&lt;/code&gt; field to an abstract model, you will get the following error:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;abstract_base_model.Cart.items: (fields.E300) Field defines a relation with model &amp;#39;Product&amp;#39;, which is either not installed, or is abstract.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A foreign key constraint can only point to a concrete table. The abstract base model &lt;code&gt;Product&lt;/code&gt; only exists in the code, so there is no products table in the database. The Django ORM will only create tables for the derived models &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;EBook&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Given that you can’t reference the abstract base class &lt;code&gt;Product&lt;/code&gt;, you need to reference books and e-books 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&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;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&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;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;books&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;ebooks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EBook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can now add both books and e-books to the cart:&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;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&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;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&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;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&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;book&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebooks&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;ebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This model is a bit more complicated now. Let’s query the total price of the items in the cart:&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;django.db.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sum&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;django.db.models.functions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Coalesce&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;Coalesce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;books__price&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;ebooks__price&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;))&lt;/span&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;go&quot;&gt;{&amp;#39;total_price&amp;#39;: 1000}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because you have more than one type of book, you use &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/models/database-functions/#coalesce&quot;&gt;&lt;code&gt;Coalesce&lt;/code&gt;&lt;/a&gt; to fetch either the price of the book or the price of the e-book for each row.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pro&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Easier to implement specific logic&lt;/strong&gt;: A separate model for each product makes it easier to implement, test, and maintain specific logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Require multiple foreign keys&lt;/strong&gt;: To reference all types of products, each type needs a foreign key.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Harder to implement and maintain&lt;/strong&gt;: Operations on all types of products require checking all foreign keys. This adds complexity to the code and makes maintenance and testing harder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Very hard to scale&lt;/strong&gt;: New types of products require additional models. Managing many models can be tedious and very hard to scale.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An abstract base model is a good choice when there are very few types of objects that required very distinct logic.&lt;/p&gt;
&lt;p&gt;An intuitive example is modeling a payment process for your online shop. You want to accept payments with credit cards, PayPal, and store credit. Each payment method goes through a very different process that requires very distinct logic. Adding a new type of payment is not very common, and you don&amp;rsquo;t plan on adding new payment methods in the near future.&lt;/p&gt;
&lt;p&gt;You create a payment process base class with derived classes for credit card payment process, PayPal payment process, and store credit payment process. For each of the derived classes, you implement the payment process in a very different way that cannot be easily shared. In this case, it might make sense to handle each payment process specifically.&lt;/p&gt;
&lt;h2 id=&quot;concrete-base-model&quot;&gt;Concrete Base Model&lt;/h2&gt;
&lt;p&gt;Django offers another way to implement inheritance in models. Instead of using an abstract base class that only exists in the code, you can make the base class concrete. &amp;ldquo;Concrete&amp;rdquo; means that the base class exists in the database as a table, unlike in the abstract base class solution, where the base class only exists in the code.&lt;/p&gt;
&lt;p&gt;Using the abstract base model, you were unable to reference multiple type of products. You were forced to create a many-to-many relation for each type of product. This made it harder to perform tasks on the common fields such as getting the total price of all the items in the cart.&lt;/p&gt;
&lt;p&gt;Using a concrete base class, Django will create a table in the database for the &lt;code&gt;Product&lt;/code&gt; model. The &lt;code&gt;Product&lt;/code&gt; model will have all the common fields you defined in the base model. Derived models such as &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;EBook&lt;/code&gt; will reference the &lt;code&gt;Product&lt;/code&gt; table using a one-to-one field. To reference a product, you create a foreign key to the base model:&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;django.contrib.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&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;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&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;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&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;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;o&quot;&gt;-&amp;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;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;name&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EBook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URLField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only difference between this example and the previous one is that the &lt;code&gt;Product&lt;/code&gt; model is not defined with &lt;code&gt;abstract=True&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To create new products, you use derived &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;EBook&lt;/code&gt; models 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;concrete_base_model.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EBook&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;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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;Python Tricks&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;price&lt;/span&gt;&lt;span class=&quot;o&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;weight&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;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;n&quot;&gt;book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: Python Tricks&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;ebook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EBook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&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 Old Man and the Sea&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;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&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;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://books.com/12345&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;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;ebook&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: The Old Man and the Sea&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the case of concrete base class, it’s interesting to see what&amp;rsquo;s happening in the underlying database. Let’s look at the tables created by Django in the database:&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\d&lt;/span&gt; concrete_base_model_product

&lt;span class=&quot;go&quot;&gt;Column |          Type          |                         Default&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--------+-----------------------+---------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;id     | integer                | nextval(&amp;#39;concrete_base_model_product_id_seq&amp;#39;::regclass)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;name   | character varying(100) |&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;price  | integer                |&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Indexes:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   &amp;quot;concrete_base_model_product_pkey&amp;quot; PRIMARY KEY, btree (id)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Referenced by:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   TABLE &amp;quot;concrete_base_model_cart_items&amp;quot; CONSTRAINT &amp;quot;...&amp;quot; FOREIGN KEY (product_id) &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   REFERENCES concrete_base_model_product(id) DEFERRABLE INITIALLY DEFERRED&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;   TABLE &amp;quot;concrete_base_model_book&amp;quot; CONSTRAINT &amp;quot;...&amp;quot; FOREIGN KEY (product_ptr_id) &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   REFERENCES concrete_base_model_product(id) DEFERRABLE INITIALLY DEFERRED&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;   TABLE &amp;quot;concrete_base_model_ebook&amp;quot; CONSTRAINT &amp;quot;...&amp;quot; FOREIGN KEY (product_ptr_id) &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   REFERENCES concrete_base_model_product(id) DEFERRABLE INITIALLY DEFERRED&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The product table has two familiar fields: name and price. These are the common fields you defined in the &lt;code&gt;Product&lt;/code&gt; model. Django also created an ID primary key for you.&lt;/p&gt;
&lt;p&gt;In the constraints section, you see multiple tables that are referencing the product table. Two tables that stand out are &lt;code&gt;concrete_base_model_book&lt;/code&gt; and &lt;code&gt;concrete_base_model_ebook&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\d&lt;/span&gt; concrete_base_model_book

&lt;span class=&quot;go&quot;&gt;    Column     |  Type&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;---------------+---------&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;product_ptr_id | integer&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;weight         | integer&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Indexes:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   &amp;quot;concrete_base_model_book_pkey&amp;quot; PRIMARY KEY, btree (product_ptr_id)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Foreign-key constraints:&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;   &amp;quot;...&amp;quot; FOREIGN KEY (product_ptr_id) REFERENCES concrete_base_model_product(id) &lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;   DEFERRABLE INITIALLY DEFERRED&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;Book&lt;/code&gt; model has only two fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;weight&lt;/code&gt;&lt;/strong&gt; is the field you added in the derived &lt;code&gt;Book&lt;/code&gt; model.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;product_ptr_id&lt;/code&gt;&lt;/strong&gt; is both the primary of the table and a foreign key to the base product model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Behind the scenes, Django created a base table for product. Then, for each derived model, Django created another table that includes the additional fields, and a field that acts both as a primary key and a foreign key to the product table.&lt;/p&gt;
&lt;p&gt;Let’s take a look at a query generated by Django to fetch a single book. Here are the results of &lt;code&gt;print(Book.objects.filter(pk=1).query)&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sql&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_product&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_product&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_product&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;price&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_book&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;product_ptr_id&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_book&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;weight&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_book&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_product&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_book&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;product_ptr_id&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_product&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;ss&quot;&gt;&amp;quot;concrete_base_model_book&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;product_ptr_id&amp;quot;&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&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To fetch a single book, Django joined &lt;code&gt;concrete_base_model_product&lt;/code&gt; and &lt;code&gt;concrete_base_model_book&lt;/code&gt; on the &lt;code&gt;product_ptr_id&lt;/code&gt; field. The name and price are in the product table and the weight is in the book table.&lt;/p&gt;
&lt;p&gt;Since all the products are managed in the Product table, you can now reference it in a foreign key from the &lt;code&gt;Cart&lt;/code&gt; model:&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;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&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;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&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;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Adding items to the cart is the same as before:&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;concrete_base_model.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&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;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&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;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ebook&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&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;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: Python Tricks&amp;gt;, &amp;lt;Book: The Old Man and the Sea&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Working with common fields is also simple:&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;django.db.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sum&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;price&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;total_price&amp;#39;: 2500}&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;Migrating base classes in Django:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When a derived model is created, Django adds a &lt;code&gt;bases&lt;/code&gt; attribute to the migration:&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;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateModel&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;Book&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fields&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;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;      &lt;span class=&quot;n&quot;&gt;bases&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;concrete_base_model.product&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,),&lt;/span&gt;
&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If in the future you remove or change the base class, Django might not be able to perform the migration automatically. You might get this error:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;TypeError: metaclass conflict: the metaclass of a derived class must &lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;be a (non-strict) subclass of the metaclasses of all its bases&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a known issue in Django (&lt;a href=&quot;https://code.djangoproject.com/ticket/23818&quot;&gt;#23818&lt;/a&gt;, &lt;a href=&quot;https://code.djangoproject.com/ticket/23521&quot;&gt;#23521&lt;/a&gt;, &lt;a href=&quot;https://code.djangoproject.com/ticket/26488&quot;&gt;#26488&lt;/a&gt;). To work around it, you must edit the original migration manually and adjust the bases attribute.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Primary key is consistent across all types&lt;/strong&gt;: The product is issued by a single sequence in the base table. This restriction can be easily resolved by using a UUID instead of a sequence.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Common attributes can be queried from a single table&lt;/strong&gt;: Common queries such as total price, list of product names, and prices can be fetched directly from the base table.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;New product types require schema changes&lt;/strong&gt;: A new type requires a new model.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Can produce inefficient queries&lt;/strong&gt;: The data for a single item is in two database tables. Fetching a product requires a join with the base table.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cannot access extended data from base class instance&lt;/strong&gt;: A type field is required to downcast an item. This adds complexity to the code. &lt;a href=&quot;https://django-polymorphic.readthedocs.io/en/stable/&quot;&gt;&lt;code&gt;django-polymorphic&lt;/code&gt;&lt;/a&gt; is a popular module that might eliminate some of these challenges.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The concrete base model approach is useful when common fields in the base class are sufficient to satisfy most common queries.&lt;/p&gt;
&lt;p&gt;For example, if you often need to query for the cart total price, show a list of items in the cart, or run ad hoc analytic queries on the cart model, you can benefit from having all the common attributes in a single database table.&lt;/p&gt;
&lt;h2 id=&quot;generic-foreign-key&quot;&gt;Generic Foreign Key&lt;/h2&gt;
&lt;p&gt;Inheritance can sometimes be a nasty business. It forces you to create (&lt;a href=&quot;https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction&quot;&gt;possibly premature&lt;/a&gt;) abstractions, and it doesn&amp;rsquo;t always fit nicely into the ORM.&lt;/p&gt;
&lt;p&gt;The main problem you have is referencing different products from the cart model. You first tried to squash all the product types into one model (sparse model, semi-structured model), and you got clutter. Then you tried splitting products into separate models and providing a unified interface using a concrete base model. You got a complicated schema and a lot of joins.&lt;/p&gt;
&lt;p&gt;Django offers a special way of referencing any model in the project called &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/#django.contrib.contenttypes.fields.GenericForeignKey&quot;&gt;&lt;code&gt;GenericForeignKey&lt;/code&gt;&lt;/a&gt;. Generic foreign keys are part of the &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/&quot;&gt;Content Types framework&lt;/a&gt; built into Django. The content type framework is used by Django itself to keep track of models. This is necessary for some core capabilities such as migrations and permissions.&lt;/p&gt;
&lt;p&gt;To better understand what content types are and how they facilitate generic foreign keys, let’s look at the content type related to the &lt;code&gt;Book&lt;/code&gt; model:&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;django.contrib.contenttypes.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&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;vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;_state&amp;#39;: &amp;lt;django.db.models.base.ModelState at 0x7f1c9ea64400&amp;gt;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;id&amp;#39;: 22,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;app_label&amp;#39;: &amp;#39;concrete_base_model&amp;#39;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;model&amp;#39;: &amp;#39;book&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each model has a unique identifier. If you want to reference a book with PK 54, you can say, “Get object with PK 54 in the model represented by content type 22.”&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GenericForeignKey&lt;/code&gt; is implemented exactly like that. To create a generic foreign key, you define two fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A reference to a content type (the model)&lt;/li&gt;
&lt;li&gt;The primary key of the referenced object (the model instance&amp;rsquo;s &lt;code&gt;pk&lt;/code&gt; attribute)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To implement a many-to-many relation using &lt;code&gt;GenericForeignKey&lt;/code&gt;, you need to manually create a model to connect carts with items.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Cart&lt;/code&gt; model remains roughly similar to what you have seen so far:&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;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&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;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&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;Unlike previous &lt;code&gt;Cart&lt;/code&gt; models, this &lt;code&gt;Cart&lt;/code&gt; no longer includes a &lt;code&gt;ManyToMany&lt;/code&gt; field.  You are going need to do that yourself.&lt;/p&gt;
&lt;p&gt;To represent a single item in the cart, you need to reference both the cart and any product:&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;django.contrib.contenttypes.fields&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GenericForeignKey&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.contenttypes.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CartItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForeignKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;related_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;items&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;n&quot;&gt;product_object_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForeignKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PROTECT&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;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GenericForeignKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;s1&quot;&gt;&amp;#39;product_content_type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;s1&quot;&gt;&amp;#39;product_object_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To add a new item in the Cart, you provide the content type and the primary key:&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;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&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;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&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;n&quot;&gt;ebook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EBook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&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;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Adding an item to a cart is a common task. You can add a method on the cart to add any product to the cart:&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;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_item&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;product&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;s1&quot;&gt;&amp;#39;CartItem&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&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;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cart&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;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&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;Adding a new item to a cart is now much shorter:&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Getting information about the items in the cart is also possible:&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&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;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;CartItem: CartItem object (1)&amp;gt;, &amp;lt;CartItem: CartItem object (2)&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;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&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;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: Python Tricks&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;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So far so good. Where&amp;rsquo;s the catch?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try to calculate the total price of the products in the cart:&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;django.db.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sum&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;product__price&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;FieldError: Field &amp;#39;product&amp;#39; does not generate an automatic reverse &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;relation and therefore cannot be used for reverse querying. &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;If it is a GenericForeignKey, consider adding a GenericRelation.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Django tells us it isn&amp;rsquo;t possible to traverse the generic relation from the generic model to the referenced model. The reason for that is that Django has no idea which table to join to. Remember, the &lt;code&gt;Item&lt;/code&gt; model can point to any &lt;code&gt;ContentType&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The error message does mention a &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/#django.contrib.contenttypes.fields.GenericRelation&quot;&gt;&lt;code&gt;GenericRelation&lt;/code&gt;&lt;/a&gt;. Using a &lt;code&gt;GenericRelation&lt;/code&gt;, you can define a reverse relation from the referenced model to the &lt;code&gt;Item&lt;/code&gt; model. For example, you can define a reverse relation from the &lt;code&gt;Book&lt;/code&gt; model to items of books:&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;django.contrib.contenttypes.fields&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GenericRelation&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cart_items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GenericRelation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&amp;#39;CartItem&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&amp;#39;product_object_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&amp;#39;product_content_type_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;related_query_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;books&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using the reverse relation, you can answer questions like how many carts include a specific book:&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;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart_items&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;go&quot;&gt;4&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;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books__id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&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;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The two statement are identical.&lt;/p&gt;
&lt;p&gt;You still need to know the price of the entire cart. You already saw that fetching the price from each product table is impossible using the ORM. To do that, you have to iterate the items, fetch each item separately, and aggregate:&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;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;price&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;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&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;go&quot;&gt;2500&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is one of the major disadvantages of generic foreign keys. The flexibility comes with a great performance cost. It&amp;rsquo;s very hard to optimize for performance using just the Django ORM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Structural Subtyping&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the abstract and concrete base class approaches, you used &lt;strong&gt;nominal subtyping&lt;/strong&gt;, which is based on a class hierarchy.  Mypy is able to detect this form of relation between two classes and infer types from it.&lt;/p&gt;
&lt;p&gt;In the generic relation approach, you used structural subtyping. &lt;strong&gt;Structural subtyping&lt;/strong&gt; exists when a class implements all the methods and attributes of another class. This form of subtyping is very useful when you wish to avoid direct dependency between modules.&lt;/p&gt;
&lt;p&gt;Mypy provides a way to utilize structural subtyping using &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/protocols.html&quot;&gt;Protocols&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You already identified a product entity with common methods and attributes. You can define a &lt;code&gt;Protocol&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;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing_extensions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Protocol&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pk&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;name&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;price&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;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;o&quot;&gt;-&amp;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;o&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; The use of class attributes and ellipses (&lt;code&gt;...&lt;/code&gt;) in the method definition are new features in Python 3.7. In earlier versions of Python, it isn&amp;rsquo;t possible to define a Protocol using this syntax. Instead of an ellipsis, methods should have &lt;code&gt;pass&lt;/code&gt; in the body. Class attributes such as &lt;code&gt;pk&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; can be defined using the &lt;code&gt;@attribute&lt;/code&gt; decorator, but it will not work with Django models.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can now use the &lt;code&gt;Product&lt;/code&gt; protocol to add type information. For example, in &lt;code&gt;add_item()&lt;/code&gt;, you accept an instance of a product and add it to the cart:&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_item&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;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&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;s1&quot;&gt;&amp;#39;CartItem&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&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;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cart&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;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&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;Running &lt;code&gt;mypy&lt;/code&gt; on this function will not yield any warnings. Let&amp;rsquo;s say you change &lt;code&gt;product.pk&lt;/code&gt; to &lt;code&gt;product.id&lt;/code&gt;, which is not defined in the &lt;code&gt;Product&lt;/code&gt; protocol:&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_item&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;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&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;s1&quot;&gt;&amp;#39;CartItem&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&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;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cart&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;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will get the following warning from Mypy:&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; mypy
&lt;span class=&quot;go&quot;&gt;models.py:62: error: &amp;quot;Product&amp;quot; has no attribute &amp;quot;id&amp;quot;&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; &lt;code&gt;Protocol&lt;/code&gt; is not yet a part of Mypy. It&amp;rsquo;s part of the complementary package called &lt;a href=&quot;https://pypi.org/project/mypy_extensions/&quot;&gt;&lt;code&gt;mypy_extentions&lt;/code&gt;&lt;/a&gt;. The package is developed by the Mypy team and includes features that they thought weren&amp;rsquo;t ready for the main Mypy package yet.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Migrations are not needed to add product types:&lt;/strong&gt; The generic foreign key can reference any model. Adding a new type of product does not require migrations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Any model can be used as an item:&lt;/strong&gt; Using generic foreign key, any model can be referenced by the &lt;code&gt;Item&lt;/code&gt; model.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Built-in admin support:&lt;/strong&gt; Django has &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/#generic-relations-in-admin&quot;&gt;built-in support for generic foreign keys in the admin&lt;/a&gt;. It can inline, for example, information about the referenced models in the detail page.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Self-contained module:&lt;/strong&gt; There is no direct dependency between the products module and the cart module. This makes this approach ideal for existing projects and pluggable modules.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Can produce inefficient queries:&lt;/strong&gt; The ORM cannot determine in advance what models are referenced by the generic foreign key. This makes it very difficult for it to optimize queries that fetch multiple types of products.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Harder to understand and maintain:&lt;/strong&gt; Generic foreign key eliminates some Django ORM features that require access to specific product models. Accessing information from the product models requires writing more code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Typing requires &lt;code&gt;Protocol&lt;/code&gt;:&lt;/strong&gt; Mypy is unable to provide type checking for generic models. A &lt;code&gt;Protocol&lt;/code&gt; is required.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Generic foreign keys are a great choice for pluggable modules or existing projects. The use of &lt;code&gt;GenericForeignKey&lt;/code&gt; and structural subtyping abstract any direct dependency between the modules.&lt;/p&gt;
&lt;p&gt;In the bookstore example, the book and e-book models can exist in a separate app and new products can be added without changing the cart module. For existing projects, a &lt;code&gt;Cart&lt;/code&gt; module can be added with minimal changes to existing code.&lt;/p&gt;
&lt;p&gt;The patterns presented in this article play nicely together. Using a mixture of patterns, you can eliminate some of the disadvantages and optimize the schema for your use case.&lt;/p&gt;
&lt;p&gt;For example, in the generic foreign key approach, you were unable to get the price of the entire cart quickly. You had to fetch each item separately and aggregate. You can address this specific concern by inlining the price of the product on the &lt;code&gt;Item&lt;/code&gt; model (the sparse model approach). This will allow you to query only the &lt;code&gt;Item&lt;/code&gt; model to get the total price very quickly.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, you started with a small town bookstore and grew it to a big e-commerce website. You tackled different types of problems and adjusted your model to accommodate the changes. You learned that problems such as complex code and difficulty adding new programmers to the team are often symptoms of a larger problem. You learned how to identify these problems and solve them.&lt;/p&gt;
&lt;p&gt;You now know how to plan and implement a polymorphic model using the Django ORM. You&amp;rsquo;re familiar with multiple approaches, and you understand their pros and cons. You&amp;rsquo;re able to analyze your use case and decide on the best course of action.&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 Corey Schafer</title>
      <id>https://realpython.com/interview-corey-schafer/</id>
      <link href="https://realpython.com/interview-corey-schafer/"/>
      <updated>2018-12-31T14:00:00+00:00</updated>
      <summary>Corey Schafer is a full-time content creator publishing regular Python tutorials on YouTube. In this interview, Corey gives his advice for budding YouTubers and content creators. He also has some thoughts about how programming and woodworking are similar!</summary>
      <content type="html">
        &lt;p&gt;For this week&amp;rsquo;s community interview, I am joined by Corey Schafer, of YouTube fame. &lt;/p&gt;
&lt;p&gt;Corey is a full-time content creator publishing regular &lt;a href=&quot;https://www.youtube.com/watch?v=OPUP4ghN9oo&quot;&gt;Python tutorials on YouTube&lt;/a&gt;. In this interview, we talk to Corey about his YouTube channel and his advice for budding YouTubers and content creators, getting his first developer job, and his passion for woodworking.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Welcome to Real Python! We might as well start at the beginning. How’d you get into programming, and when did you start using Python?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3 rounded-circle&quot; src=&quot;https://files.realpython.com/media/corey.62669baa4dba.jpg&quot; width=&quot;864&quot; height=&quot;864&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/corey.62669baa4dba.jpg&amp;amp;w=216&amp;amp;sig=26c64351c6ea562ecf42ac421387058867a3dc97 216w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/corey.62669baa4dba.jpg&amp;amp;w=432&amp;amp;sig=f116094fbdee47b63655f820cd21a18f0009acc1 432w, https://files.realpython.com/media/corey.62669baa4dba.jpg 864w&quot; sizes=&quot;75vw&quot; alt=&quot;Corey Schafer&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Corey:&lt;/strong&gt; Thanks for having me. I actually got started programming a little later in life than most people you&amp;rsquo;ve probably interviewed. I used to be a little self-conscious about getting started later, but now I try to be upfront about it so that others aren&amp;rsquo;t as intimidated if they are just getting started at an &amp;ldquo;older&amp;rdquo; age.&lt;/p&gt;
&lt;p&gt;I suppose I technically got started in college when I began my degree in Computer Science, but even at that time, I wasn&amp;rsquo;t taking it very seriously. I would do enough to pass tests, but I wasn&amp;rsquo;t absorbing any of the information. I definitely wasn&amp;rsquo;t doing any side projects or using coding in any real-world applications.&lt;/p&gt;
&lt;p&gt;It wasn&amp;rsquo;t until my mid-20s that I randomly applied to an internship for NASA at Kennedy Space Center and started to take programming seriously. To my surprise, I was chosen for the internship. Imposter syndrome set in full-force on day 1. I was definitely out of my league.&lt;/p&gt;
&lt;p&gt;But the longer I worked there, the more I realized that these people weren&amp;rsquo;t superhuman. These were people just like me, with the exception that they&amp;rsquo;ve spent much more time putting in the work to master their skill.&lt;/p&gt;
&lt;p&gt;I thought to myself, &amp;ldquo;If they can do it, there&amp;rsquo;s no reason I can&amp;rsquo;t.&amp;rdquo; So I left Cape Canaveral with a newfound motivation to really dive into programming and learn as much as I could. I really didn&amp;rsquo;t feel like I could call myself an actual programmer until my late 20s. I am in my early 30s now, so I feel like I haven&amp;rsquo;t even scratched the surface in terms of the skills I would like the master.&lt;/p&gt;
&lt;p&gt;As far as learning Python, I didn&amp;rsquo;t start using it until about 4 years ago. I was a full-time front-end JavaScript developer at the West Virginia University GIS Technical Center doing some mapping work. We were using Python for some of the backend scripts, and I was assigned to maintain/update some of those. I found that I enjoyed that much more than the front-end JavaScript work and began using Python on a daily basis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;You mentioned that you came to programming a little later in life (though still young). With that context in mind, what was your experience getting your first junior developer job, and do you have any advice for anyone looking for their first developer job later in life?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Corey:&lt;/strong&gt; I got my first development job in my mid-20s, after my internship in Cape Canaveral. That job was at a small research company in West Virginia (WV). The competition isn&amp;rsquo;t as fierce in WV as it is in places like San Francisco or Silicon Valley, so I was able to land a position without much prior experience.&lt;/p&gt;
&lt;p&gt;I was extremely intimidated when I first started there. I never wanted anyone to see any code that I had written out of fear that it would expose me as not knowing nearly as much as anyone else. I later came to find out that this is a common fear within the community.&lt;/p&gt;
&lt;p&gt;Actually working as a developer full-time taught me much more than anything I had learned in school or self-study. Knowing the fundamentals is definitely helpful, but there&amp;rsquo;s no replacement for actually writing real-world applications and having your code critiqued by people who have been doing this for years.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s definitely uncomfortable for your mistakes to be on display for your coworkers to see, but once you get over that discomfort, you come out on the other side less likely to make the same mistakes in the future. Many of my viewers ask me, &amp;ldquo;How do you just know how to solve these problems?&amp;rdquo; Well, the truth is, many problems in programming are very similar.&lt;/p&gt;
&lt;p&gt;Once you solve problems incorrectly over the years and are shown better and better ways, you eventually learn to recognize certain patterns and use the most efficient methods from the start. This isn&amp;rsquo;t a skill that most people have naturally. It is developed over years of trial and error.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Most of our readers might recognize you from your very popular YouTube channel. I certainly heard of you through it. Not only is it on our &lt;a href=&quot;https://realpython.com/python-youtube-channels/&quot;&gt;Ultimate List of Python YouTube Channels&lt;/a&gt;, but I regularly watch your videos and even built one of my most recent Flask apps as a direct result of your Flask course. How have you found YouTube as a platform from a teaching point of view? Any surprising lessons learned?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Corey:&lt;/strong&gt; Thanks. I was honored to have &lt;a href=&quot;https://www.youtube.com/user/schafer5/featured&quot;&gt;my channel&lt;/a&gt; featured in your article. YouTube is a terrific platform for learning, and I&amp;rsquo;m excited to see where online learning evolves in the future. Websites like YouTube have definitely lowered the barrier-to-entry for anyone who wants to create content.&lt;/p&gt;
&lt;p&gt;Many people think you need a nice recording studio or some financial backing before you get started, but that is no longer the case. As long as you have a computer and a cell phone then you basically have all of the tools you need to get started. I have upgraded my equipment over time, but I first started on YouTube by doing screen recordings on a cheap laptop using the built-in laptop microphone.&lt;/p&gt;
&lt;p&gt;For anyone who is thinking about starting a YouTube channel or creating content in general, I do have some lessons I&amp;rsquo;ve learned over time. I believe the most important lesson I&amp;rsquo;ve learned is that you should make content for yourself. I try not to create tutorials for topics for the sole purpose of them being popular or what will get the most views&amp;hellip; I instead try to create the lessons that I wish I had when learning that topic.&lt;/p&gt;
&lt;p&gt;Take notes while you learn certain subjects and keep track of what you found difficult to digest and why. If you get stuck on something, then most likely others are getting stuck on it as well. And once you find solutions to these problems, then you can look back and see if there is a way anyone could have explained it to you that would have helped you understand more easily. If there is, then be sure to pass that on to others.&lt;/p&gt;
&lt;p&gt;I really believe that advice carries over into other fields. If you&amp;rsquo;re an educator, then make content that you personally would have found helpful. If you are a musician, then make music that you personally enjoy. If you&amp;rsquo;re a comedian, then tell jokes that you think are funny. If you do that, then most likely there are many people out there like yourself who will share your same mindset and love your content.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;What’s next for the YouTube channel going forward? Any plans to branch out into paid courses or other forms of teaching?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Corey:&lt;/strong&gt; At the moment, I am sticking to YouTube courses. I feel very fortunate to have a platform that allows me to put my content out there for free and be funded by ad revenue. This allows people who can&amp;rsquo;t afford courses or who can&amp;rsquo;t pay for content at the moment to get access to all of my content.&lt;/p&gt;
&lt;p&gt;Ideally, my content will always remain free as long as I have enough coming in to live on. It also wouldn&amp;rsquo;t be possible without generous supporters contributing through sites like &lt;a href=&quot;https://www.patreon.com/coreyms&quot;&gt;Patreon&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/user/schafer5/featured&quot;&gt;YouTube Channel Memberships&lt;/a&gt;. They support me financially on a monthly basis so that the content can remain free for those who can&amp;rsquo;t afford it. I would love to continue with this model into the future for as long as I can.&lt;/p&gt;
&lt;p&gt;As far as branching out into other forms of teaching, I have thought about creating an online learning platform of some kind. I have used a lot of tools in my personal life that have helped me learn topics quickly. Tools such as spaced-repetition-learning applications and daily coding challenges.&lt;/p&gt;
&lt;p&gt;I would love to tackle a project that brought these concepts to a learning platform in a way that would help students absorb material more quickly and fully. Any type of project like that will be something I wouldn&amp;rsquo;t work on for some time though. For now, I am focused on creating video content.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;You, sir, are a talented woodworker! Your stuff looks impressive. Is this a new hobby or a long-time passion of yours? How did you get started?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Corey:&lt;/strong&gt; Oh, thank you for saying that. It is a hobby that I wish I had more time to explore. It is something I have been toying with for many years now. Personally, it is a fantastic way to clear my mind and drain any stress that&amp;rsquo;s built up.&lt;/p&gt;
&lt;p&gt;Woodworking and programming can be similar in many ways. Sometimes I will start a woodworking project and have an idea of what I want the finished product to look like, but not a detailed understanding of where to begin. So you start by jotting down some outlines, then knock out the smaller components, and then after many hours of work you can combine all of that together into the result you were hoping for. There&amp;rsquo;s a lot of pride that comes with finishing those types of projects.&lt;/p&gt;
&lt;p class=&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? Any you’d like to share and/or plug?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Corey:&lt;/strong&gt; Since I spend my professional career at a computer, I like to spend my spare time outside as much as possible. This includes hiking, kayaking, camping, swimming, and trips to the dog park. Now that I am working from home and have a more flexible schedule, my girlfriend and I would love to start traveling more. I believe I might mark some international PyCons on my calendar and use those as an excuse to visit some places we have wanted to see for a very long time, with the added bonus of meeting more folks from the Python community.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Thank you, Corey, for joining me this week. You can find Corey&amp;rsquo;s &lt;a href=&quot;https://www.youtube.com/user/schafer5/featured&quot;&gt;YouTube channel here&lt;/a&gt;. You can get in touch with him via &lt;a href=&quot;https://twitter.com/CoreyMSchafer&quot;&gt;Twitter&lt;/a&gt; or &lt;a href=&quot;http://coreyms.com/&quot;&gt;his website&lt;/a&gt;. And if you&amp;rsquo;ve benefited from Corey&amp;rsquo;s videos, consider supporting his efforts with via &lt;a href=&quot;https://www.patreon.com/coreyms&quot;&gt;Patreon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If there&amp;rsquo;s someone you&amp;rsquo;d like me to interview from the Python community, then reach out to me in the comments below, or &lt;a href=&quot;https://twitter.com/EndlessTrax&quot;&gt;send me a message on Twitter&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>Make a Location-Based Web App With Django and GeoDjango</title>
      <id>https://realpython.com/location-based-app-with-geodjango-tutorial/</id>
      <link href="https://realpython.com/location-based-app-with-geodjango-tutorial/"/>
      <updated>2018-12-24T14:00:00+00:00</updated>
      <summary>In this step-by-step Python tutorial, you’ll learn how to use Django and GeoDjango to build a location-based web application from scratch. You’ll be building a simple nearby shops application that lists the shops closest to a user’s location.</summary>
      <content type="html">
        &lt;p&gt;Throughout this tutorial, you&amp;rsquo;ll learn how to use Django and GeoDjango to build a location-based web application from scratch. You&amp;rsquo;ll be building a simple nearby shops application that lists the shops closest to a user&amp;rsquo;s location. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this tutorial, you&amp;rsquo;ll be able to:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use Django to build a simple web application from scratch&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use the GeoDjango sub-framework to implement geolocation features in your Django application&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use a spatial database (PostgreSQL and PostGIS) to get benefits from the spatial features and easily implement location-aware web apps&lt;/p&gt;
&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-django-resources-2&quot; data-focus=&quot;false&quot;&gt;Click here to get free access to additional Django tutorials and resources&lt;/a&gt; you can use to deepen your Python web development skills.&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;Example App Source Code:&lt;/strong&gt; The full source code for the app you&amp;rsquo;ll be building in this tutorial is available on the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/nearbyshops&quot;&gt;&lt;code&gt;realpython/materials&lt;/code&gt; repository on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;the-tools-you-will-be-using&quot;&gt;The Tools You Will Be Using&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ll be using the following tools to develop your nearby shops web application:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Python programming language&lt;/li&gt;
&lt;li&gt;The Django web framework&lt;/li&gt;
&lt;li&gt;The PostgreSQL database for persisting data&lt;/li&gt;
&lt;li&gt;The PostGIS extension for supporting spatial features in the PostgreSQL database&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip&lt;/code&gt; for installing dependencies&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;venv&lt;/code&gt; module for managing a virtual environment&lt;/li&gt;
&lt;li&gt;Docker for installing PostgreSQL and PostGIS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Untitled_Diagram_8.822b7357a376.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-75&quot; src=&quot;https://files.realpython.com/media/Untitled_Diagram_8.822b7357a376.png&quot; width=&quot;900&quot; height=&quot;1060&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Untitled_Diagram_8.822b7357a376.png&amp;amp;w=225&amp;amp;sig=8aa88185ff04109c31cd45dde80fed5b57ea98a3 225w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Untitled_Diagram_8.822b7357a376.png&amp;amp;w=450&amp;amp;sig=e4a20c0b710fd810241e7363d3854954c6c98f7a 450w, https://files.realpython.com/media/Untitled_Diagram_8.822b7357a376.png 900w&quot; sizes=&quot;75vw&quot; alt=&quot;Used tools&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Before diving into practical steps, let&amp;rsquo;s first start by introducing the frameworks you&amp;rsquo;ll be using.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; is the most popular Python framework for building web apps. It makes it easy for developers to quickly build prototypes and meet their project deadlines by providing a plethora of built-in APIs and sub-frameworks such as GeoDjango. &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/&quot;&gt;GeoDjango&lt;/a&gt; is a built-in application that is included as a &lt;code&gt;contrib&lt;/code&gt; module in Django. It&amp;rsquo;s actually a complete framework itself that can also be used separately from Django. It provides a toolbox of utilities for building GIS web applications.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Geographic_information_system&quot;&gt;GIS&lt;/a&gt; stands for Geographic Information System. It&amp;rsquo;s an information system (&lt;a href=&quot;https://en.wikipedia.org/wiki/Information_system&quot;&gt;an organized system for the collection, organization, storage, and communication of information&lt;/a&gt;) designed for processing and manipulating data that have geographic or spatial characteristics.&lt;/p&gt;
&lt;p&gt;GeoDjango also provides Python bindings to popular spatial libraries such as &lt;a href=&quot;https://trac.osgeo.org/geos/&quot;&gt;GEOS&lt;/a&gt;, &lt;a href=&quot;https://www.gdal.org/&quot;&gt;GDAL&lt;/a&gt;, and &lt;a href=&quot;https://github.com/maxmind/geoip-api-c&quot;&gt;GeoIP&lt;/a&gt;, which can be used separately without Django in any Python application or interactively in the shell.&lt;/p&gt;
&lt;p&gt;GeoDjango aims to provide a world-class geographic web framework. It has been refactored over the years with the goal of making it easier to work with geospatial data, in other words data that identifies the geographic location of natural or artificial features on Earth and is stored as coordinates and topologies.&lt;/p&gt;
&lt;p&gt;GeoDjango integrates very well with the Django ORM and provides a set of geometry fields defined by the &lt;a href=&quot;http://www.opengeospatial.org/&quot;&gt;Open Geospatial Consortium (OGS)&lt;/a&gt; that can be used to map to different types of geometries in geospatial databases: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/model-api/#geometryfield&quot;&gt;&lt;code&gt;GeometryField&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; is the base class for all geometric fields in GeoDjango.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/model-api/#pointfield&quot;&gt;&lt;code&gt;PointField&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; is used for storing GEOS &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/geos/#django.contrib.gis.geos.Point&quot;&gt;Point&lt;/a&gt; objects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/model-api/#polygonfield&quot;&gt;&lt;code&gt;PolygonField&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; is used for storing GEOS &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/geos/#polygon&quot;&gt;Polygon&lt;/a&gt; objects and so on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GeoDjango is a very powerful framework for storing and working with geographic data using the Django ORM. It provides an easy-to-use API to find the distances between two points on a map, areas of polygons, the points within a polygon, and so on.&lt;/p&gt;
&lt;p&gt;To be able to work with GeoDjango, you&amp;rsquo;ll need to have two things: a spatial database and geospatial libraries. A &lt;a href=&quot;https://en.wikipedia.org/wiki/Spatial_database&quot;&gt;spatial database&lt;/a&gt; is a database that is optimized for storing and querying data that represents objects defined in a geometric space.&lt;/p&gt;
&lt;p&gt;To fully use all features of GeoDjango, you&amp;rsquo;ll need to install the following open-source geospatial libraries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GEOS&lt;/strong&gt; stands for Geometry Engine Open Source. It&amp;rsquo;s a C++ port of the JTS (Java Topology Suite) that implements the &lt;a href=&quot;http://www.opengeospatial.org/standards/sfs&quot;&gt;OCG Simple Feature for SQL specification&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GDAL&lt;/strong&gt; stands for Geospatial Data Abstraction Library. It&amp;rsquo;s an open-source library for working with raster and vector &lt;a href=&quot;https://en.wikipedia.org/wiki/GIS_file_formats&quot;&gt;geospatial data formats&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://proj4.org/&quot;&gt;&lt;strong&gt;PROJ.4&lt;/strong&gt;&lt;/a&gt; is for Cartographic Projections library. It&amp;rsquo;s an open-source GIS library for easily working with spatial reference systems and projections.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GeoIP&lt;/strong&gt; is a library that helps users find geographical information based on an IP address.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This tutorial makes use of an Ubuntu 18.04 system for installing prerequisites and a Unix bash for running the commands, but this shouldn&amp;rsquo;t be a problem for you if you&amp;rsquo;re using any other system, particularly Unix-based systems like macOS.&lt;/p&gt;
&lt;p&gt;For most installation instructions, you&amp;rsquo;ll be using the &lt;a href=&quot;https://help.ubuntu.com/lts/serverguide/aptitude.html.en&quot;&gt;aptitude&lt;/a&gt; package manager, so you should simply replace that with the equivalent package manager for your system.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll be installing the prerequisites needed before you can bootstrap your project, such as Python 3 and GeoDjango dependencies (GEOS, GDAL, and PROJ.4). You&amp;rsquo;ll also use Docker to set up a PostgreSQL and PostGIS database for your project.&lt;/p&gt;
&lt;h3 id=&quot;installing-python-3&quot;&gt;Installing Python 3&lt;/h3&gt;
&lt;p&gt;There is a big chance that you already have Python 3 installed on your system. If you don&amp;rsquo;t, you can simply head to the &lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;official website&lt;/a&gt; and download the binaries for your operating system.&lt;/p&gt;
&lt;p&gt;Depending on your system, you may also be able to install Python 3 or upgrade it to the latest version if it&amp;rsquo;s already installed by using the official package manager.&lt;/p&gt;
&lt;p&gt;If you have a problem installing Python 3 or want more information, you can check the &lt;a href=&quot;https://realpython.com/installing-python&quot;&gt;Python 3 Installation &amp;amp; Setup Guide&lt;/a&gt;, which provides different ways to install Python 3 on your system.&lt;/p&gt;
&lt;p&gt;Finally, you can check if you have Python 3 installed 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; python3 --version
&lt;span class=&quot;go&quot;&gt;Python 3.6.5&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;installing-geodjango-dependencies-geos-gdal-and-proj4&quot;&gt;Installing GeoDjango Dependencies (GEOS, GDAL, and PROJ.4)&lt;/h3&gt;
&lt;p&gt;GeoDjango requires a spatial database and a set of open-source geospatial libraries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GEOS&lt;/strong&gt; is an open-source geometry engine and a C++ port of the JTS (Java Topology Suite). It&amp;rsquo;s required by GeoDjango for performing geometric operations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PROJ.4&lt;/strong&gt; is an open-source GIS library for easily working with spatial reference systems and projections. You need it because you&amp;rsquo;ll be using PostGIS as the spatial database.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GDAL&lt;/strong&gt; is an open-source geospatial data abstraction library for working with raster and vector data formats. It&amp;rsquo;s needed for many utilities used by GeoDjango.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can refer to the docs for more information about &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/install/geolibs/&quot;&gt;spatial databases and the required libraries&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://launchpad.net/ubuntu/bionic/+source/gdal&quot;&gt;GDAL 2.2&lt;/a&gt; is included in Ubuntu 18.04 so you can simply run the following command to install it:&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 aptitude install gdal-bin libgdal-dev
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; sudo aptitude install python3-gdal
&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; &lt;code&gt;python3-gdal&lt;/code&gt; is the Python 3 binding for GDAL.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Next, you can install the other libraries using 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 aptitude install binutils libproj-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;Note:&lt;/strong&gt; Since you&amp;rsquo;re using a binary package for GEOS, you also need to &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/install/#binutils&quot;&gt;install binutils&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Refer to the docs for detailed instructions about how to install these dependencies on &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/install/#macos&quot;&gt;macOS&lt;/a&gt; and &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/install/#windows&quot;&gt;Windows&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For more information about PROJ.4, you can refer to its official &lt;a href=&quot;https://proj4.org/install.html&quot;&gt;docs&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;setting-up-a-spatial-database-with-postgresql-and-postgis&quot;&gt;Setting up a Spatial Database With PostgreSQL and PostGIS&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ll use PostgreSQL, the most commonly used database with Django. It&amp;rsquo;s not a spatial database, but thanks to PostGIS you can power up your database with powerful geospatial features.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://postgis.net/&quot;&gt;PostGIS&lt;/a&gt; is a spatial database extension that needs to be installed on a &lt;a href=&quot;https://www.postgresql.org/&quot;&gt;PostgreSQL&lt;/a&gt; database, which gives it the capability to store and work with spatial data and perform spatial operations. It adds support for geographic objects allowing location queries to be run in SQL.&lt;/p&gt;
&lt;p&gt;You can either install PostgreSQL on your system, create a database, and then add the PostGIS extension, or better yet use Docker to quickly create a database using the &lt;a href=&quot;https://hub.docker.com/r/kartoza/postgis/&quot;&gt;kartoza postgis&lt;/a&gt; image, which provides a container with PostgreSQL and PostGIS already 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; docker run --name&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;postgis -d -e &lt;span class=&quot;nv&quot;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;user001 -e &lt;span class=&quot;nv&quot;&gt;POSTGRES_PASS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;123456789&lt;/span&gt; -e &lt;span class=&quot;nv&quot;&gt;POSTGRES_DBNAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;gis -p &lt;span class=&quot;m&quot;&gt;5432&lt;/span&gt;:5432 kartoza/postgis:9.6-2.4
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After running the command, you&amp;rsquo;ll have a PostgreSQL server listening on the &lt;code&gt;5432&lt;/code&gt; port with a database called &lt;code&gt;gis&lt;/code&gt;. The database uses the &lt;code&gt;user001&lt;/code&gt; username and the &lt;code&gt;123456789&lt;/code&gt; password.&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 need to have Docker installed on your system. For instructions, you can simply refer to the official &lt;a href=&quot;https://docs.docker.com/install/&quot;&gt;docs&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;setting-up-your-project&quot;&gt;Setting up Your Project&lt;/h2&gt;
&lt;p&gt;Now that you have a spatial database set up and ready, you can go ahead and setup your Django project. In this section, you&amp;rsquo;ll use &lt;code&gt;venv&lt;/code&gt; to create an isolated virtual environment for your project and install all the required packages such as Django. &lt;/p&gt;
&lt;h3 id=&quot;creating-a-virtual-environment&quot;&gt;Creating a Virtual Environment&lt;/h3&gt;
&lt;p&gt;A virtual environment allows you to create an isolated environment for the dependencies of your current project. This will allow you to avoid conflicts between the same packages that have different versions. &lt;/p&gt;
&lt;p&gt;In Python 3, you can create virtual environments using &lt;code&gt;virtualenv&lt;/code&gt; or the &lt;code&gt;venv&lt;/code&gt; module. &lt;/p&gt;
&lt;p&gt;For more information about Python virtual environments, 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;Now, head over to your terminal and run the following command to create a virtual environment based on Python 3:&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 -m venv env
&lt;span class=&quot;gp&quot;&gt;(env) $&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, you need to activate 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; &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; env/bin/activate
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s it. You now have your virtual environment activated, and you can install the packages for your project.&lt;/p&gt;
&lt;h3 id=&quot;installing-django&quot;&gt;Installing Django&lt;/h3&gt;
&lt;p&gt;The first step after creating and activating a virtual environment is to install Django. The Django package is available from the &lt;a href=&quot;https://pypi.org/&quot;&gt;Python Package Index&lt;/a&gt; (PyPI) so you can simply use &lt;code&gt;pip&lt;/code&gt; to install it by running the following command in your terminal:&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 django
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;creating-a-django-project&quot;&gt;Creating a Django Project&lt;/h3&gt;
&lt;p&gt;The project you&amp;rsquo;ll be creating is a web application that lists shops sorted by distance so your users will be able to discover the shops that are close to their location. &lt;/p&gt;
&lt;p&gt;The web application makes use of GeoDjango for easily implementing location requirements like calculating the distances of shops from the user&amp;rsquo;s location and ordering the shops by distance. &lt;/p&gt;
&lt;p&gt;Using GeoDjango, you can get and display the nearest shops that are stored in a PostgreSQL database configured with the PostGIS extension to enable spatial operations.&lt;/p&gt;
&lt;p&gt;Now, you&amp;rsquo;re ready to create a Django project using the &lt;code&gt;django-admin.py&lt;/code&gt; script. Simply 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; django-admin.py startproject nearbyshops
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create a project named &lt;code&gt;nearbyshops&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;configuring-the-postgresql-database&quot;&gt;Configuring the PostgreSQL Database&lt;/h3&gt;
&lt;p&gt;Now that you&amp;rsquo;ve created a project, let&amp;rsquo;s continue by configuring the connection to the PostgreSQL and PostGIS spatial database. Open the &lt;code&gt;settings.py&lt;/code&gt; file and add &lt;code&gt;django.contrib.gis.db.backends.postgis&lt;/code&gt; as the engine with the credentials for the PostGIS database you configured 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;DATABASES&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;default&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;s1&quot;&gt;&amp;#39;ENGINE&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.gis.db.backends.postgis&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;gis&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&amp;#39;USER&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;user001&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&amp;#39;PASSWORD&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;123456789&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&amp;#39;HOST&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&amp;#39;PORT&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;5432&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;/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; You need to change the database credentials accordingly if you didn&amp;rsquo;t specify the same credentials when running the Docker container.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If you try to run your Django server at this point, you&amp;rsquo;ll get the &lt;code&gt;ImportError: No module named &#39;psycopg2&#39;&lt;/code&gt; error related to &lt;code&gt;psycopg2&lt;/code&gt;, which is the most popular PostgreSQL adapter for Python. To solve the error, you simply need to install the &lt;code&gt;psycopg2-binary&lt;/code&gt; in your virtual environment using 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 psycopg2-binary
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;adding-geodjango&quot;&gt;Adding GeoDjango&lt;/h3&gt;
&lt;p&gt;GeoDjango is a framework that makes it as easy as possible to build GIS and location aware web applications. You can add it by simply including the &lt;code&gt;gis&lt;/code&gt; &lt;code&gt;contrib&lt;/code&gt; module in the list of installed apps. &lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;settings.py&lt;/code&gt; file and locate the &lt;code&gt;INSTALLED_APPS&lt;/code&gt; array. Then add the &lt;code&gt;&#39;django.contrib.gis&#39;&lt;/code&gt; module:&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;INSTALLED_APPS&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;c1&quot;&gt;# [...]&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.gis&amp;#39;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;creating-a-django-application&quot;&gt;Creating a Django Application&lt;/h2&gt;
&lt;p&gt;A Django project is made up of applications. By default, it contains several core or built-in apps like &lt;code&gt;django.contrib.admin&lt;/code&gt;, but you will usually add at least one app that contains your custom project&amp;rsquo;s code. &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; For simple projects, you may only need one app, but once your project becomes bigger and has different requirements, you can organize your code in multiple separate apps. &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now that you have created a Django project, configured the connection with the spatial database, and added GeoDjango to the project, you need to create a Django application that you may call &lt;code&gt;shops&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;shops&lt;/code&gt; application will contain the code for creating and displaying the shops closest to a user&amp;rsquo;s location. In the next steps, you are going to perform the following tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create the app&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;Shop&lt;/code&gt; model&lt;/li&gt;
&lt;li&gt;Add a data migration for loading initial demo data (shops)&lt;/li&gt;
&lt;li&gt;Add a view function&lt;/li&gt;
&lt;li&gt;Add a template&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First run the following command to create the app:&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 manage.py startapp shops
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, you need to add it to the list of installed apps in the &lt;code&gt;settings.py&lt;/code&gt; file, which will make Django recognize it as a part of your project:&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;INSTALLED_APPS&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;c1&quot;&gt;# [...]&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;shops&amp;#39;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;creating-a-django-model&quot;&gt;Creating a Django Model&lt;/h3&gt;
&lt;p&gt;After creating the &lt;code&gt;shops&lt;/code&gt; application, which will contain the actual code of your project, you need to add models in your app. Django uses an ORM (Object Relational Mapper), which is an abstraction layer between Django and the database that transforms Python objects (or models) into database tables.&lt;/p&gt;
&lt;p&gt;In this case, you need one model that represents a shop in the database. You&amp;rsquo;ll create a &lt;code&gt;Shop&lt;/code&gt; model that has the following fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;:&lt;/strong&gt; the name of the shop &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;location&lt;/code&gt;:&lt;/strong&gt; the location of the shop in latitude and longitude coordinates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;address&lt;/code&gt;:&lt;/strong&gt; the address of the shop&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;city&lt;/code&gt;:&lt;/strong&gt; the city the shop is in&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open the &lt;code&gt;shops/models.py&lt;/code&gt; file and add 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;django.contrib.gis.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Shop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&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;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&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;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PointField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&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;n&quot;&gt;city&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;For the location, you are using the &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/model-api/#pointfield&quot;&gt;&lt;code&gt;PointField&lt;/code&gt;&lt;/a&gt;, a GeoDjango-specific geometric field for storing a &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/geos/#django.contrib.gis.geos.Point&quot;&gt;GEOS Point&lt;/a&gt; object that represents a pair of longitude and latitude coordinates.&lt;/p&gt;
&lt;p&gt;The other fields are normal Django fields of type &lt;code&gt;CharField&lt;/code&gt; that can be used to store strings of small and large size.&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; Please note that the &lt;code&gt;models&lt;/code&gt; module is imported from &lt;code&gt;django.contrib.gis.db&lt;/code&gt; and not the usual &lt;code&gt;django.db&lt;/code&gt; module.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;creating-the-database-tables&quot;&gt;Creating the Database Tables&lt;/h3&gt;
&lt;p&gt;With Django, you don&amp;rsquo;t need to use SQL to create the database tables thanks to its ORM. Let&amp;rsquo;s create the database tables by using the &lt;code&gt;makemigrations&lt;/code&gt; and &lt;code&gt;migrate&lt;/code&gt; commands. Head back to your terminal and 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; python manage.py makemigrations
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For more information about these commands, check out &lt;a href=&quot;https://realpython.com/django-migrations-a-primer/&quot;&gt;Django Migrations – A Primer&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;adding-a-super-user&quot;&gt;Adding a Super User&lt;/h3&gt;
&lt;p&gt;You need to create a super user so you can access the admin interface. This can be done using 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; python manage.py createsuperuser
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The prompt will ask you for the username, email, and password you want to use for accessing the user account. Enter them and hit &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;h3 id=&quot;registering-the-model-in-the-admin-interface&quot;&gt;Registering the Model in the Admin Interface&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/admin/&quot;&gt;Django&amp;rsquo;s admin application&lt;/a&gt; provides a complete CRUD interface for managing data. &lt;/p&gt;
&lt;p&gt;GeoDjango extends the admin application to add support for working with geometry fields.&lt;/p&gt;
&lt;p&gt;Before you can access your models from Django admin, you need to register them.&lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;shops/admin.py&lt;/code&gt; file and add 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;django.contrib.gis.admin&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OSMGeoAdmin&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Shop&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Shop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ShopAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OSMGeoAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;list_display&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;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;location&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You are using the &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/admin/#the-register-decorator&quot;&gt;&lt;code&gt;@admin.register&lt;/code&gt;&lt;/a&gt; decorator to register the &lt;code&gt;Shop&lt;/code&gt; model in the admin application. The decorated class is a representation of the &lt;code&gt;Shop&lt;/code&gt; model in the admin interface and allows you to customize different aspects such as the &lt;code&gt;Shop&lt;/code&gt; fields that you want to display. (In your case, it&amp;rsquo;s the name and location.) For more information about decorators, you can read  &lt;a href=&quot;https://realpython.com/primer-on-python-decorators/&quot;&gt;Primer on Python Decorators&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since the &lt;code&gt;Shop&lt;/code&gt; model includes a GeoDjango field, you need to use the special &lt;code&gt;OSMGeoAdmin&lt;/code&gt; class that&amp;rsquo;s available from the &lt;code&gt;django.contrib.gis.admin&lt;/code&gt; package .&lt;/p&gt;
&lt;p&gt;You can either use &lt;code&gt;GeoModelAdmin&lt;/code&gt; or &lt;code&gt;OSMGeoAdmin&lt;/code&gt;, which is a subclass of &lt;code&gt;GeoModelAdmin&lt;/code&gt; that uses an &lt;a href=&quot;https://www.openstreetmap.org/&quot;&gt;Open Street Map&lt;/a&gt; layer in the admin to display geometric fields. This provides more information like street and thoroughfare details than would be available with the &lt;code&gt;GeoModelAdmin&lt;/code&gt; class, which uses &lt;a href=&quot;http://earth-info.nga.mil/publications/vmap0.html&quot;&gt;Vector Map Level 0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can now run the Django server:&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 manage.py runserver
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You application will be running from &lt;code&gt;localhost:8000&lt;/code&gt;, and you can access the admin interface from &lt;code&gt;localhost:8000/admin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-28_21-22-03.34e252c9266a.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-100&quot; src=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-28_21-22-03.34e252c9266a.png&quot; width=&quot;691&quot; height=&quot;341&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-28_21-22-03.34e252c9266a.png&amp;amp;w=172&amp;amp;sig=dd3dba40987769483c987a21a92b24ecf3136dc9 172w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-28_21-22-03.34e252c9266a.png&amp;amp;w=345&amp;amp;sig=d8e928cebf82284cd3a0bb33ca87bf9ba1e659bd 345w, https://files.realpython.com/media/Screenshot_from_2018-11-28_21-22-03.34e252c9266a.png 691w&quot; sizes=&quot;75vw&quot; alt=&quot;Admin interface, shop app&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a screenshot from the &lt;em&gt;Add shop&lt;/em&gt; interface: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-28_21-12-25.2e7a3ff3f939.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-100&quot; src=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-28_21-12-25.2e7a3ff3f939.png&quot; width=&quot;968&quot; height=&quot;654&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-28_21-12-25.2e7a3ff3f939.png&amp;amp;w=242&amp;amp;sig=7f6b240fc7134de35fadb91e4307248ba2f1d7b7 242w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-28_21-12-25.2e7a3ff3f939.png&amp;amp;w=484&amp;amp;sig=d2d7bf4d220f408af1d55f6a12b533bb72407ad9 484w, https://files.realpython.com/media/Screenshot_from_2018-11-28_21-12-25.2e7a3ff3f939.png 968w&quot; sizes=&quot;75vw&quot; alt=&quot;Admin interface, add shop&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can see that the &lt;code&gt;Location&lt;/code&gt; geometric field is displayed as an interactive map. You can zoom the map in and out, and you can choose different selectors at the top right corner of the map to select a location, which is marked by the green circle. &lt;/p&gt;
&lt;h3 id=&quot;adding-initial-data&quot;&gt;Adding Initial Data&lt;/h3&gt;
&lt;p&gt;You need some initial demo data for your application, but instead of manually adding data, you can use a data migration.&lt;/p&gt;
&lt;p&gt;Data migrations can be used for multiple scenarios, including adding initial data in your database. For more information, check out &lt;a href=&quot;https://realpython.com/data-migrations/&quot;&gt;Data Migrations&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before creating a migration, let&amp;rsquo;s first get some real-world data from &lt;a href=&quot;http://www.openstreetmap.org/&quot;&gt;OpenStreetMap&lt;/a&gt; using &lt;a href=&quot;https://overpass-turbo.eu/&quot;&gt;overpass turbo&lt;/a&gt;, a web-based data filtering tool for OpenStreetMap. You can run &lt;a href=&quot;http://wiki.openstreetmap.org/wiki/Overpass_API&quot;&gt;Overpass API&lt;/a&gt; queries and analyze the resulting data interactively on the map.&lt;/p&gt;
&lt;p&gt;You can also use the integrated &lt;a href=&quot;http://wiki.openstreetmap.org/wiki/Overpass_turbo/Wizard&quot;&gt;Wizard&lt;/a&gt;, which makes it easy to create queries. &lt;/p&gt;
&lt;p&gt;In your case, you want to get all the shops in a city. Simply click on the &lt;em&gt;Wizard&lt;/em&gt; button. A small window will pop up. In the text field, write a query like &amp;ldquo;shop in Miami&amp;rdquo; and click on &lt;em&gt;build and run query&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-09_22-55-41.7a5b7e60974c.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-66&quot; src=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-09_22-55-41.7a5b7e60974c.png&quot; width=&quot;352&quot; height=&quot;245&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-09_22-55-41.7a5b7e60974c.png&amp;amp;w=88&amp;amp;sig=3c2d1c39c668182b7c833737ef51be794e7f73e7 88w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-09_22-55-41.7a5b7e60974c.png&amp;amp;w=176&amp;amp;sig=e160db08aa21ecab092a07d80c7ca96ee313c6b8 176w, https://files.realpython.com/media/Screenshot_from_2018-11-09_22-55-41.7a5b7e60974c.png 352w&quot; sizes=&quot;75vw&quot; alt=&quot;Overpass turbo query&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Next, click on the &lt;em&gt;export&lt;/em&gt; button and click on &lt;em&gt;download/copy as raw OSM data&lt;/em&gt; to download a JSON file that contains the raw OSM data. Save the file as &lt;code&gt;data.json&lt;/code&gt; in your project&amp;rsquo;s root folder:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_from_2018-10-19_02-25-43.ac6770d6a956.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-66&quot; src=&quot;https://files.realpython.com/media/Screenshot_from_2018-10-19_02-25-43.ac6770d6a956.png&quot; width=&quot;344&quot; height=&quot;468&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-10-19_02-25-43.ac6770d6a956.png&amp;amp;w=86&amp;amp;sig=d8c421ce85aead36496a7425d88eb4cdb01bbf0a 86w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-10-19_02-25-43.ac6770d6a956.png&amp;amp;w=172&amp;amp;sig=37765bfdae412d26d4a681018da3b5e3b85c4d4a 172w, https://files.realpython.com/media/Screenshot_from_2018-10-19_02-25-43.ac6770d6a956.png 344w&quot; sizes=&quot;75vw&quot; alt=&quot;Overpass turbo export&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a screenshot of example data from the file:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_from_2018-10-19_02-34-34.dce4b6e8d898.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-75&quot; src=&quot;https://files.realpython.com/media/Screenshot_from_2018-10-19_02-34-34.dce4b6e8d898.png&quot; width=&quot;589&quot; height=&quot;364&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-10-19_02-34-34.dce4b6e8d898.png&amp;amp;w=147&amp;amp;sig=f3ec0e13b3044da4d5fb7581945406f3f5647b62 147w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-10-19_02-34-34.dce4b6e8d898.png&amp;amp;w=294&amp;amp;sig=643f905571cec1055e8c9ee4062b74ce32ae748e 294w, https://files.realpython.com/media/Screenshot_from_2018-10-19_02-34-34.dce4b6e8d898.png 589w&quot; sizes=&quot;75vw&quot; alt=&quot;Overpass turbo data example&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You need to get the objects in the &lt;code&gt;elements&lt;/code&gt; array. Specifically the &lt;code&gt;lat&lt;/code&gt;, &lt;code&gt;lon&lt;/code&gt;, and &lt;code&gt;tags&lt;/code&gt; (&lt;code&gt;name&lt;/code&gt;) fields for each shop.&lt;/p&gt;
&lt;p&gt;You can find more details on how you can write Overpass queries from this 
&lt;a href=&quot;http://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL&quot;&gt;wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s create an empty migration for importing the content of the &lt;code&gt;data.json&lt;/code&gt; file in the database, using 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; python manage.py makemigrations shops --empty
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Open the migration file. It has 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;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dependencies&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;shops&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;0001_initial&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;n&quot;&gt;operations&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;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You next need to create &lt;code&gt;load_data()&lt;/code&gt; to be executed by &lt;code&gt;RunPython()&lt;/code&gt;. First, in the import area, add the following imports:&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;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;json&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.gis.geos&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromstr&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pathlib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You are importing the &lt;code&gt;Path&lt;/code&gt; class from the &lt;a href=&quot;https://docs.python.org/3/library/pathlib.html&quot;&gt;&lt;code&gt;pathlib&lt;/code&gt;&lt;/a&gt; package for accessing low-level system functions, the &lt;a href=&quot;https://docs.python.org/3.7/library/json.html&quot;&gt;&lt;code&gt;json&lt;/code&gt;&lt;/a&gt; package for working with JSON, the Django built-in migrations API, and &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/geos/#django.contrib.gis.geos.fromstr&quot;&gt;&lt;code&gt;fromstr()&lt;/code&gt;&lt;/a&gt;, part of the &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/geos/&quot;&gt;&lt;code&gt;geos&lt;/code&gt;&lt;/a&gt; package.&lt;/p&gt;
&lt;p&gt;Next, add &lt;code&gt;load_data()&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;DATA_FILENAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;data.json&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;load_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;schema_editor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Shop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;shops&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Shop&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;jsonfile&lt;/span&gt; &lt;span class=&quot;o&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;vm&quot;&gt;__file__&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;parents&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;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DATA_FILENAME&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;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonfile&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;datafile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;objects&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;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datafile&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;obj&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;elements&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;objType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;node&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;tags&amp;#39;&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;n&quot;&gt;tags&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;no-name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;longitude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&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;lon&amp;#39;&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;latitude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&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;lat&amp;#39;&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;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromstr&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;POINT(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{longitude}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{latitude}&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;n&quot;&gt;srid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4326&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Shop&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;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&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;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;KeyError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;     
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let&amp;rsquo;s explain the code you&amp;rsquo;ve just added. You first construct the absolute path using the &lt;code&gt;Path&lt;/code&gt; class of the &lt;a href=&quot;https://realpython.com/python-pathlib/&quot;&gt;&lt;code&gt;pathlib&lt;/code&gt; library&lt;/a&gt; and open the &lt;code&gt;data.json&lt;/code&gt; file. Next, you parse the JSON file into a Python object. &lt;/p&gt;
&lt;p&gt;You loop through the elements object containing the locations and tags of shops. Inside the loop, you extract the name and the longitude and latitude coordinates. Then you use &lt;code&gt;formstr()&lt;/code&gt; to return a valid &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/geos/#django.contrib.gis.geos.GEOSGeometry&quot;&gt;&lt;code&gt;GEOSGeometry&lt;/code&gt;&lt;/a&gt; object corresponding to the spatial data in the string that can be assigned to the location field of the &lt;code&gt;Shop&lt;/code&gt; model. Finally, you create and save the instance of the &lt;code&gt;Shop&lt;/code&gt; model corresponding to the extracted data.&lt;/p&gt;
&lt;p&gt;You are using the &lt;a href=&quot;https://dbader.org/blog/python-context-managers-and-with-statement&quot;&gt;&lt;code&gt;with&lt;/code&gt; statement&lt;/a&gt;, so you don&amp;rsquo;t have to explicitly close the file, and an &lt;a href=&quot;https://realpython.com/python-f-strings/&quot;&gt;f-string&lt;/a&gt; for formatting the argument of &lt;code&gt;fromstr()&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;fromstr()&lt;/code&gt; takes a &lt;code&gt;srid&lt;/code&gt; as the second parameter. &lt;a href=&quot;https://en.wikipedia.org/wiki/Spatial_reference_system&quot;&gt;&lt;code&gt;srid&lt;/code&gt;&lt;/a&gt; stands for Spatial Reference System Identifier. It&amp;rsquo;s a unique value to identify spatial reference systems (projection systems used for interpreting the data in the spatial database). &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;4326&lt;/code&gt; &lt;code&gt;srid&lt;/code&gt; is the most popular system used with PostGIS. It&amp;rsquo;s also known as &lt;a href=&quot;https://en.wikipedia.org/wiki/World_Geodetic_System#WGS84&quot;&gt;WGS84&lt;/a&gt;, where units are specified in degrees of longitude and latitude. You can refer to &lt;a href=&quot;http://spatialreference.org/&quot;&gt;spatialreference.org&lt;/a&gt; for a Django-powered database of spatial reference systems. &lt;/p&gt;
&lt;p&gt;Next, add the migration class to execute the above function when you run the &lt;code&gt;migrate&lt;/code&gt; command:&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;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;dependencies&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;shops&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;0005_auto_20181018_2050&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;n&quot;&gt;operations&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;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunPython&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load_data&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;That&amp;rsquo;s it. You can now return to your terminal and 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; python manage.py migrate
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The data from the &lt;code&gt;data.json&lt;/code&gt; file will be loaded on your database. Run your Django server and head to your admin interface. You should see your data in the table. In my case, this is a screenshot of a portion of the table:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-28_21-09-59.c94fbe2ef0dd.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-100&quot; src=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-28_21-09-59.c94fbe2ef0dd.png&quot; width=&quot;1007&quot; height=&quot;538&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-28_21-09-59.c94fbe2ef0dd.png&amp;amp;w=251&amp;amp;sig=4710f0003c4f0759b016e36ccc0b3e00fc9cf715 251w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-28_21-09-59.c94fbe2ef0dd.png&amp;amp;w=503&amp;amp;sig=2534c92212baeabb7708207ceb408652d6901036 503w, https://files.realpython.com/media/Screenshot_from_2018-11-28_21-09-59.c94fbe2ef0dd.png 1007w&quot; sizes=&quot;75vw&quot; alt=&quot;Admin interface, list shops&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;displaying-nearby-shops&quot;&gt;Displaying Nearby Shops&lt;/h3&gt;
&lt;p&gt;At this point of the tutorial, you have created:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;shops&lt;/code&gt; application, which encapsulates the code for creating and getting nearby shops in your project&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;Shop&lt;/code&gt; model and the corresponding tables in the database&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The initial admin user for accessing the admin interface&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The initial demo data for loading real-world shops in the database that you can play with without manually entering a lot of fake data&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You also registered the &lt;code&gt;Shop&lt;/code&gt; model in the admin application so you can create, update, delete, and list shops from the admin interface. &lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll add a view function using the generic &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-display/#django.views.generic.list.ListView&quot;&gt;&lt;code&gt;ListView&lt;/code&gt;&lt;/a&gt; class that you can use to display a list of nearby shops. You also create an HTML template that will be used by the view function to render the shops and add the URL that will be used to display the shops.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start by adding a template and a view function that will be used to display the nearby shops from a user&amp;rsquo;s location. &lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;shops/views.py&lt;/code&gt; file and start by importing the necessary APIs:&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;django.views&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.gis.geos&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromstr&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.gis.db.models.functions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Shop&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next add a &lt;code&gt;user_location&lt;/code&gt; variable where you can hard code a user location: &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;longitude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;80.191788&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;latitude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;25.761681&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;user_location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;srid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4326&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this part, you&amp;rsquo;ll simply hard code the user&amp;rsquo;s location (the coordinates of Miami in the USA), but this ideally should be specified by the user or retrieved automatically from the user&amp;rsquo;s browser with their permission using JavaScript and the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API&quot;&gt;HTML5 GeoLocation API&lt;/a&gt;. You can scroll down to the middle of that page  to see a live example of implementing the Geolocation API. &lt;/p&gt;
&lt;p&gt;Finally, add the following view 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Home&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Shop&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context_object_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;shops&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;queryset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;annotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;location&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user_location&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;distance&amp;#39;&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;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;template_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;shops/index.html&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You are using the generic class-based &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-display/#django.views.generic.list.ListView&quot;&gt;&lt;code&gt;ListView&lt;/code&gt;&lt;/a&gt; to create a view.&lt;/p&gt;
&lt;p&gt;Class-based views are an alternative way to implement views as Python classes instead of functions. They are used to handle common use cases in web development without re-inventing the wheel. In this example, you&amp;rsquo;ve just sub-classed the &lt;code&gt;ListView&lt;/code&gt; generic view and overridden the &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;context_object_name&lt;/code&gt;, &lt;code&gt;queryset&lt;/code&gt;, and &lt;code&gt;template_name&lt;/code&gt; attributes to create a list view that handles HTTP requests without any extra code.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s focus on the &lt;code&gt;queryset&lt;/code&gt; attribute. To get the nearby shops, you simply use &lt;code&gt;.annotate()&lt;/code&gt; to annotate each object on the returned queryset with a distance annotation that&amp;rsquo;s calculated using &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/functions/#distance&quot;&gt;&lt;code&gt;Distance()&lt;/code&gt;&lt;/a&gt;, available from GeoDjango, between the location of each shop and the user&amp;rsquo;s location. You also order the returned queryset by the distance annotation and take only the nearest six shops.&lt;/p&gt;
&lt;p&gt;You can learn more about class-based views from the official &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/topics/class-based-views/&quot;&gt;docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next let&amp;rsquo;s add the &lt;code&gt;shops/index.html&lt;/code&gt; template with the following content:&lt;/p&gt;
&lt;div class=&quot;highlight html&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Nearby Shops&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Nearby Shops&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    {% if shops %}
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    {% for shop in shops %}
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        {{ shop.name }}: {{shop.distance}}
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    {% endfor %}
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    {% endif %}
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The nearest shops are available from the &lt;code&gt;shops&lt;/code&gt; context object that you specified as the &lt;code&gt;context_object_name&lt;/code&gt; in the class-based view. You loop through the &lt;code&gt;shops&lt;/code&gt; object and you display the name and distance from the user&amp;rsquo;s location for each shop.&lt;/p&gt;
&lt;p&gt;Finally, let&amp;rsquo;s add a URL to our &lt;code&gt;urls.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;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.urls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;shops&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;urlpatterns&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;c1&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;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShopList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;as_view&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;You use &lt;code&gt;.as_view()&lt;/code&gt; to return a callable view that takes a &lt;code&gt;request&lt;/code&gt; and returns a &lt;code&gt;response&lt;/code&gt;, which can be passed as the second parameter for &lt;code&gt;path()&lt;/code&gt; that maps paths to views.&lt;/p&gt;
&lt;p&gt;Now you can run your Django server. The home page will display a simple un-styled list with the nearest shops from the hard-coded user&amp;rsquo;s location. This is an example screenshot:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-28_21-13-19.35e78f378050.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-75&quot; src=&quot;https://files.realpython.com/media/Screenshot_from_2018-11-28_21-13-19.35e78f378050.png&quot; width=&quot;491&quot; height=&quot;205&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-28_21-13-19.35e78f378050.png&amp;amp;w=122&amp;amp;sig=6a7df69060c71848e0f74677063062a5b94134e1 122w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_from_2018-11-28_21-13-19.35e78f378050.png&amp;amp;w=245&amp;amp;sig=3581564fc93bcb39449b06aaf7d9a93058e3c77f 245w, https://files.realpython.com/media/Screenshot_from_2018-11-28_21-13-19.35e78f378050.png 491w&quot; sizes=&quot;75vw&quot; alt=&quot;Nearby shops&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the screenshot, each item in the list displays the name of the shop (before the colon) and the distance in meters from the user&amp;rsquo;s location (after the colon). The letter &lt;em&gt;m&lt;/em&gt; refers to meters.&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 view function you used to display the results in the screenshot is just for testing the queryset annotated by &lt;code&gt;Distance()&lt;/code&gt;. &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In the next part of the series, you&amp;rsquo;ll use a RESTful endpoint to return the nearest shops. Instead of a Django template, you&amp;rsquo;ll use Vue.js with some CSS styling for a better look, so stay tuned!&lt;/p&gt;
&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap-Up&lt;/h2&gt;
&lt;p&gt;Congratulations on creating your location based web application using GeoDjango, which aims to become a world-class geographic framework for implementing GIS apps. You now have the basic skills that you can use to either add simple geolocation stuff to your applications or create GIS apps. You can read the &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/gis/&quot;&gt;GeoDjango docs&lt;/a&gt; for a complete resource of the available APIs and what you can do with them.&lt;/p&gt;
&lt;p&gt;You also learned to use &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; to quickly pull and launch a PostgreSQL and PostGIS server. Docker can be used for more than that. It&amp;rsquo;s a containerization tool for spinning up isolated, reproducible application environments. You can read &lt;a href=&quot;https://realpython.com/django-development-with-docker-compose-and-machine/&quot;&gt;Django Development with Docker Compose and Machine&lt;/a&gt; if you want to learn how to containerize your Django project. &lt;/p&gt;
&lt;p&gt;Nowadays, location aware apps (apps that know your location and help you discover nearby objects and services by offering you results based on your location) are all the rage. Using the knowledge you gained in this tutorial, you&amp;rsquo;ll be able to incorporate this modern feature in your apps developed with Django.&lt;/p&gt;
&lt;p&gt;The only requirement, besides the dependencies of GeoDjango, is to use a spatial database (a database that&amp;rsquo;s capable of storing and manipulating spatial data). For PostgreSQL, one of the most popular database management systems used with Django, you can simply install the &lt;a href=&quot;https://postgis.net/&quot;&gt;PostGIS extension&lt;/a&gt; with your database to turn it into a spatial database. Other popular databases like &lt;a href=&quot;https://docs.oracle.com/cd/B28359_01/appdev.111/b28400/sdo_intro.htm&quot;&gt;Oracle&lt;/a&gt; and &lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/spatial-types.html&quot;&gt;MySQL&lt;/a&gt; have built-in support for spatial data.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Throughout this tutorial, you used PostgreSQL, PostGIS, Django, and GeoDjango to build a simple nearby shops web application. &lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Example App Source Code:&lt;/strong&gt; The full source code for the app you&amp;rsquo;ll be building in this tutorial is available on the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/nearbyshops&quot;&gt;&lt;code&gt;realpython/materials&lt;/code&gt; repository on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/div&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-django-resources-2&quot; data-focus=&quot;false&quot;&gt;Click here to get free access to additional Django tutorials and resources&lt;/a&gt; you can use to deepen your Python web development skills.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;In the next tutorial, you&amp;rsquo;ll continue building the application with Django REST framework to expose a RESTful API, and you&amp;rsquo;ll use the JavaScript Vue.js library with the Axios HTTP client to consume the API and render the data. &lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll also use the HTML5 Geolocation API to automatically get the user&amp;rsquo;s location from the user&amp;rsquo;s browser instead of hard-coding it like you did in this tutorial, so stay tuned!&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 Write Beautiful Python Code With PEP 8</title>
      <id>https://realpython.com/python-pep8/</id>
      <link href="https://realpython.com/python-pep8/"/>
      <updated>2018-12-19T14:00:00+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.</summary>
      <content type="html">
        &lt;p&gt;PEP 8, sometimes spelled PEP8 or PEP-8, is a document that provides guidelines and best practices on how to write Python code. It was written in 2001 by Guido van Rossum, Barry Warsaw, and Nick Coghlan. The primary focus of PEP 8 is to improve the readability and consistency of Python code.&lt;/p&gt;
&lt;p&gt;PEP stands for Python Enhancement Proposal, and there are several of them. A PEP is a document that describes new features proposed for Python and documents aspects of Python, like design and style, for the community.&lt;/p&gt;
&lt;p&gt;This tutorial outlines the key guidelines laid out in PEP 8. It&amp;rsquo;s aimed at beginner to intermediate programmers, and as such I have not covered some of the most advanced topics. You can learn about these by reading the full &lt;a href=&quot;https://www.python.org/dev/peps/pep-0008/&quot;&gt;PEP 8&lt;/a&gt; documentation. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this tutorial, you&amp;rsquo;ll be able to&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write Python code that conforms to PEP 8&lt;/li&gt;
&lt;li&gt;Understand the reasoning behind the guidelines laid out in PEP 8&lt;/li&gt;
&lt;li&gt;Set up your development environment so that you can start writing PEP 8 compliant Python code&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;why-we-need-pep-8&quot;&gt;Why We Need PEP 8&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Readability counts.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; &lt;em&gt;The Zen of Python&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;PEP 8 exists to improve the readability of Python code. But why is readability so important? Why is writing readable code one of the guiding principles of the Python language?&lt;/p&gt;
&lt;p&gt;As Guido van Rossum said, &amp;ldquo;Code is read much more often than it is written.&amp;rdquo; You may spend a few minutes, or a whole day, writing a piece of code to process user authentication. Once you&amp;rsquo;ve written it, you&amp;rsquo;re never going to write it again. But you&amp;rsquo;ll definitely have to read it again. That piece of code might remain part of a project you&amp;rsquo;re working on. Every time you go back to that file, you&amp;rsquo;ll have to remember what that code does and why you wrote it, so readability matters.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re new to Python, it can be difficult to remember what a piece of code does a few days, or weeks, after you wrote it. If you follow PEP 8, you can be sure that you&amp;rsquo;ve named your variables well. You&amp;rsquo;ll know that you&amp;rsquo;ve added enough whitespace so it&amp;rsquo;s easier to follow logical steps in your code. You&amp;rsquo;ll also have commented your code well. All this will mean your code is more readable and easier to come back to. As a beginner, following the rules of PEP 8 can make learning Python a much more pleasant task.&lt;/p&gt;
&lt;p&gt;Following PEP 8 is particularly important if you&amp;rsquo;re looking for a development job. Writing clear, readable code shows professionalism. It&amp;rsquo;ll tell an employer that you understand how to structure your code well.&lt;/p&gt;
&lt;p&gt;If you have more experience writing Python code, then you may need to collaborate with others. Writing readable code here is crucial. Other people, who may have never met you or seen your coding style before, will have to read and understand your code. Having guidelines that you follow and recognize will make it  easier for others to read your code.&lt;/p&gt;
&lt;h2 id=&quot;naming-conventions&quot;&gt;Naming Conventions&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Explicit is better than implicit.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; &lt;em&gt;The Zen of Python&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When you write Python code, you have to name a lot of things: variables, functions, classes, packages, and so on. Choosing sensible names will save you time and energy later. You&amp;rsquo;ll be able to figure out, from the name, what a certain variable, function, or class represents. You&amp;rsquo;ll also avoid using inappropriate names that might result in errors that are difficult to debug.&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;: Never use &lt;code&gt;l&lt;/code&gt;, &lt;code&gt;O&lt;/code&gt;, or &lt;code&gt;I&lt;/code&gt; single letter names as these can be mistaken for &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;0&lt;/code&gt;, depending on typeface:&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;O&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;c1&quot;&gt;# This may look like you&amp;#39;re trying to reassign 2 to zero&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;h3 id=&quot;naming-styles&quot;&gt;Naming Styles&lt;/h3&gt;
&lt;p&gt;The table below outlines some of the common naming styles in Python code and when you should use them: &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;Type&lt;/th&gt;
&lt;th&gt;Naming Convention&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Function&lt;/td&gt;
&lt;td&gt;Use a lowercase word or words. Separate words by underscores to improve readability.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;function&lt;/code&gt;, &lt;code&gt;my_function&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Variable&lt;/td&gt;
&lt;td&gt;Use a lowercase single letter, word, or words. Separate words with underscores to improve readability.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x&lt;/code&gt;, &lt;code&gt;var&lt;/code&gt;, &lt;code&gt;my_variable&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Class&lt;/td&gt;
&lt;td&gt;Start each word with a capital letter. Do not separate words with underscores. This style is called camel case.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Model&lt;/code&gt;, &lt;code&gt;MyClass&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;Use a lowercase word or words. Separate words with underscores to improve readability.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;class_method&lt;/code&gt;, &lt;code&gt;method&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constant&lt;/td&gt;
&lt;td&gt;Use an uppercase single letter, word, or words. Separate words with underscores to improve readability.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CONSTANT&lt;/code&gt;, &lt;code&gt;MY_CONSTANT&lt;/code&gt;, &lt;code&gt;MY_LONG_CONSTANT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Module&lt;/td&gt;
&lt;td&gt;Use a short, lowercase word or words. Separate words with underscores to improve readability.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;module.py&lt;/code&gt;, &lt;code&gt;my_module.py&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package&lt;/td&gt;
&lt;td&gt;Use a short, lowercase word or words. Do not separate words with underscores.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;package&lt;/code&gt;, &lt;code&gt;mypackage&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;These are some of the common naming conventions and examples of how to use them. But in order to write readable code, you still have to be careful with your choice of letters and words. In addition to choosing the correct naming styles in your code, you also have to choose the names carefully. Below are a few pointers on how to do this as effectively as possible.&lt;/p&gt;
&lt;h3 id=&quot;how-to-choose-names&quot;&gt;How to Choose Names&lt;/h3&gt;
&lt;p&gt;Choosing names for your variables, functions, classes, and so forth can be challenging. You should put a fair amount of thought into your naming choices when writing code as it will make your code more readable. The best way to name your objects in Python is to use descriptive names to make it clear what the object represents.&lt;/p&gt;
&lt;p&gt;When naming variables, you may be tempted to choose simple, single-letter lowercase names, like &lt;code&gt;x&lt;/code&gt;. But, unless you&amp;rsquo;re using &lt;code&gt;x&lt;/code&gt; as the argument of a mathematical function, it&amp;rsquo;s not clear what &lt;code&gt;x&lt;/code&gt; represents. Imagine you are storing a person&amp;rsquo;s name as a string, and you want to use string slicing to format their name differently. You could end up with something like 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;c1&quot;&gt;# Not recommended&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;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;John Smith&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;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&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;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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;Smith, John&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will work, but you&amp;rsquo;ll have to keep track of what &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;z&lt;/code&gt; represent. It may also be confusing for collaborators. A much clearer choice of names would be something like 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;c1&quot;&gt;# Recommended&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;s1&quot;&gt;&amp;#39;John Smith&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;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&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;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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;Smith, John&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Similarly, to reduce the amount of typing you do, it can be tempting to use abbreviations when choosing names. In the example below, I have defined a function &lt;code&gt;db()&lt;/code&gt; that takes a single argument &lt;code&gt;x&lt;/code&gt; and doubles 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;c1&quot;&gt;# Not recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db&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;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;mi&quot;&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At first glance, this could seem like a sensible choice. &lt;code&gt;db()&lt;/code&gt; could easily be an abbreviation for double. But imagine coming back to this code in a few days. You may have forgotten what you were trying to achieve with this function, and that would make guessing how you abbreviated it difficult.&lt;/p&gt;
&lt;p&gt;The following example is much clearer. If you come back to this code a couple of days after writing it, you&amp;rsquo;ll still be able to read and understand the purpose of this 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;c1&quot;&gt;# Recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;multiply_by_two&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;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;mi&quot;&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The same philosophy applies to all other data types and objects in Python. Always try to use the most concise but descriptive names possible. &lt;/p&gt;
&lt;h2 id=&quot;code-layout&quot;&gt;Code Layout&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Beautiful is better than ugly.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; &lt;em&gt;The Zen of Python&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;How you lay out your code has a huge role in how readable it is. In this section, you&amp;rsquo;ll learn how to add vertical whitespace to improve the readability of your code. You&amp;rsquo;ll also learn how to handle the 79 character line limit recommended in PEP 8. &lt;/p&gt;
&lt;h3 id=&quot;blank-lines&quot;&gt;Blank Lines&lt;/h3&gt;
&lt;p&gt;Vertical whitespace, or blank lines, can greatly improve the readability of your code. Code that&amp;rsquo;s bunched up together can be overwhelming and hard to read. Similarly, too many blank lines in your code makes it look very sparse, and the reader might need to scroll more than necessary. Below are three key guidelines on how to use vertical whitespace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Surround top-level functions and classes with two blank lines.&lt;/strong&gt; Top-level functions and classes should be fairly self-contained and handle separate functionality. It makes sense to put extra vertical space around them, so that it&amp;rsquo;s clear they are separate:&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;MyFirstClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MySecondClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;top_level_function&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;strong&gt;Surround method definitions inside classes with a single blank line.&lt;/strong&gt; Inside a class, functions are all related to one another. It&amp;rsquo;s good practice to leave only a single line between them:&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;MyClass&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;first_method&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;kc&quot;&gt;None&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;second_method&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;kc&quot;&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Use blank lines sparingly inside functions to show clear steps.&lt;/strong&gt; Sometimes, a complicated function has to complete several steps before the &lt;code&gt;return&lt;/code&gt; statement. To help the reader understand the logic inside the function, it can be helpful to leave a blank line between each step.&lt;/p&gt;
&lt;p&gt;In the example below, there is a function to calculate the variance of a list. This is two-step problem, so I have indicated each step by leaving a blank line between them. There is also a blank line before the &lt;code&gt;return&lt;/code&gt; statement. This helps the reader clearly see what&amp;rsquo;s returned:&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;calculate_variance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sum_list&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;number&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sum_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum_list&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;n&quot;&gt;mean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum_list&lt;/span&gt; &lt;span class=&quot;o&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;number_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;sum_squares&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;number&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sum_squares&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum_squares&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;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mean_squares&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum_squares&lt;/span&gt; &lt;span class=&quot;o&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;number_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;n&quot;&gt;mean_squares&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mean&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;If you use vertical whitespace carefully, it can greatly improved the readability of your code. It helps the reader visually understand how your code splits up into sections, and how those sections relate to one another. &lt;/p&gt;
&lt;h3 id=&quot;maximum-line-length-and-line-breaking&quot;&gt;Maximum Line Length and Line Breaking&lt;/h3&gt;
&lt;p&gt;PEP 8 suggests lines should be limited to 79 characters. This is because it allows you to have multiple files open next to one another, while also avoiding line wrapping.&lt;/p&gt;
&lt;p&gt;Of course, keeping statements to 79 characters or less is not always possible. PEP 8 outlines ways to allow statements to run over several lines.&lt;/p&gt;
&lt;p&gt;Python will assume line continuation if code is contained within parentheses, brackets, or braces:&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;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;arg_three&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_four&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;arg_one&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If it is impossible to use implied continuation, then you can use backslashes to break lines instead:&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;mypkg&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;example1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; \
    &lt;span class=&quot;n&quot;&gt;example2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;example3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, if you can use implied continuation, then you should do so.&lt;/p&gt;
&lt;p&gt;If line breaking needs to occur around binary operators, like &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt;, it should occur before the operator. This rule stems from mathematics. Mathematicians agree that breaking before binary operators improves readability. Compare the following two examples.&lt;/p&gt;
&lt;p&gt;Below is an example of breaking before a binary operator:&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;# Recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;total&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;first_variable&lt;/span&gt;
         &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second_variable&lt;/span&gt;
         &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;third_variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can immediately see which variable is being added or subtracted, as the operator is right next to the variable being operated on.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s see an example of breaking after a binary operator:&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;# Not Recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;total&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;first_variable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;second_variable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;third_variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, it&amp;rsquo;s harder to see which variable is being added and which is subtracted. &lt;/p&gt;
&lt;p&gt;Breaking before binary operators produces more readable code, so PEP 8 encourages it. Code that &lt;em&gt;consistently&lt;/em&gt; breaks after a binary operator is still PEP 8 compliant. However, you&amp;rsquo;re  encouraged to break before a binary operator.&lt;/p&gt;
&lt;h2 id=&quot;indentation&quot;&gt;Indentation&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;There should be one&amp;mdash;and preferably only one&amp;mdash;obvious way to do it.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; &lt;em&gt;The Zen of Python&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Indentation, or leading whitespace, is extremely important in Python. The indentation level of lines of code in Python determines how statements are grouped together.&lt;/p&gt;
&lt;p&gt;Consider the following 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;3&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;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;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;x is larger than 5&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 indented &lt;code&gt;print&lt;/code&gt; statement lets Python know that it should only be executed if the &lt;code&gt;if&lt;/code&gt; statement returns &lt;code&gt;True&lt;/code&gt;. The same indentation applies to tell Python what code to execute when a function is called or what code belongs to a given class.&lt;/p&gt;
&lt;p&gt;The key indentation rules laid out by PEP 8 are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use 4 consecutive spaces to indicate indentation.&lt;/li&gt;
&lt;li&gt;Prefer spaces over tabs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tabs-vs-spaces&quot;&gt;Tabs vs. Spaces&lt;/h3&gt;
&lt;p&gt;As mentioned above, you should use spaces instead of tabs when indenting code. You can adjust the settings in your text editor to output 4 spaces instead of a tab character, when you press the &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-tab&quot;&gt;Tab&lt;/kbd&gt;&lt;/span&gt; key.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re using Python 2 and have used a mixture of tabs and spaces to indent your code, you won&amp;rsquo;t see errors when trying to run it. To help you to check consistency, you can add a &lt;code&gt;-t&lt;/code&gt; flag when running Python 2 code from the command line. The interpreter will issue warnings when you are inconsistent with your use of tabs and spaces:&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; python2 -t code.py
&lt;span class=&quot;go&quot;&gt;code.py: inconsistent use of tabs and spaces in indentation&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If, instead, you use the &lt;code&gt;-tt&lt;/code&gt; flag, the interpreter will issue errors instead of warnings, and your code will not run. The benefit of using this method is that the interpreter tells you where the inconsistencies are:&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; python2 -tt code.py
&lt;span class=&quot;go&quot;&gt;  File &amp;quot;code.py&amp;quot;, line 3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    print(i, j)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;             ^&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;TabError: inconsistent use of tabs and spaces in indentation&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Python 3 does not allow mixing of tabs and spaces. Therefore, if you are using Python 3, then these errors are issued automatically:&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 code.py
&lt;span class=&quot;go&quot;&gt;  File &amp;quot;code.py&amp;quot;, line 3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    print(i, j)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;              ^&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;TabError: inconsistent use of tabs and spaces in indentation&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can write Python code with either tabs or spaces indicating indentation. But, if you&amp;rsquo;re using Python 3, you must be consistent with your choice. Otherwise, your code will not run. PEP 8 recommends that you always use 4 consecutive spaces to indicate indentation.&lt;/p&gt;
&lt;h3 id=&quot;indentation-following-line-breaks&quot;&gt;Indentation Following Line Breaks&lt;/h3&gt;
&lt;p&gt;When you&amp;rsquo;re using line continuations to keep lines to under 79 characters, it is useful to use indentation to improve readability. It allows the reader to distinguish between two lines of code and a single line of code that spans two lines. There are two styles of indentation you can use.&lt;/p&gt;
&lt;p&gt;The first of these is to align the indented block with the opening delimiter:&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;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;arg_three&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_four&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;arg_one&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sometimes you can find that only 4 spaces are needed to align with the opening delimiter. This will often occur in &lt;code&gt;if&lt;/code&gt; statements that span multiple lines as the &lt;code&gt;if&lt;/code&gt;, space, and opening bracket make up 4 characters. In this case, it can be difficult to determine where the nested code block inside the &lt;code&gt;if&lt;/code&gt; statement begins:&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;5&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&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;10&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;x&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, PEP 8 provides two alternatives to help improve readability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Add a comment after the final condition. Due to syntax highlighting in most editors, this will separate the conditions from the nested 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;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;k&quot;&gt;if&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&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;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Both conditions satisfied&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;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add extra indentation on the line continuation:&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;5&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&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;10&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;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An alternative style of indentation following a line break is a &lt;strong&gt;hanging indent&lt;/strong&gt;. This is a typographical term meaning that every line but the first in a paragraph or statement is indented. You can use a hanging indent to visually represent a continuation of a line of code. Here&amp;rsquo;s an 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;var&lt;/span&gt; &lt;span class=&quot;o&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;n&quot;&gt;arg_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arg_three&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_four&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;: When you&amp;rsquo;re using a hanging indent, there must not be any arguments on the first line. The following example is not PEP 8 compliant:&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;# Not Recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;o&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;n&quot;&gt;arg_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arg_three&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_four&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;When using a hanging indent, add extra indentation to distinguish the continued line from code contained inside the function. The following example is difficult to read because the code inside the function is at the same indentation level as the continued lines:&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;# Not Recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arg_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arg_three&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_four&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;arg_one&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Instead, it&amp;rsquo;s better to use a double indent on the line continuation. This helps you to distinguish between function arguments and the function body, improving readability:&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;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;arg_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;arg_three&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg_four&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;arg_one&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you write PEP 8 compliant code, the 79 character line limit forces you to add line breaks in your code. To improve readability, you should indent a continued line to show that it is a continued line. There are two ways of doing this. The first is to align the indented block with the opening delimiter. The second is to use a hanging indent. You are free to chose which method of indentation you use following a line break.&lt;/p&gt;
&lt;h3 id=&quot;where-to-put-the-closing-brace&quot;&gt;Where to Put the Closing Brace&lt;/h3&gt;
&lt;p&gt;Line continuations allow you to break lines inside parentheses, brackets, or braces. It&amp;rsquo;s easy to forget about the closing brace, but it&amp;rsquo;s important to put it somewhere sensible. Otherwise, it can confuse the reader. PEP 8 provides two options for the position of the closing brace in implied line continuations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Line up the closing brace with the first non-whitespace character of the previous 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;n&quot;&gt;list_of_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;1&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;3&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;5&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;7&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;mi&quot;&gt;9&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Line up the closing brace with the first character of the line that starts the construct:&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;list_of_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;1&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;3&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;5&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;7&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;mi&quot;&gt;9&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You are free to chose which option you use. But, as always, consistency is key, so try to stick to one of the above methods.&lt;/p&gt;
&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;If the implementation is hard to explain, it&amp;rsquo;s a bad idea.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; &lt;em&gt;The Zen of Python&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You should use comments to document code as it&amp;rsquo;s written. It is important to document your code so that you, and any collaborators, can understand it. When you or someone else reads a comment, they should be able to easily understand the code the comment applies to and how it fits in with the rest of your code. &lt;/p&gt;
&lt;p&gt;Here are some key points to remember when adding comments to your code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Limit the line length of comments and docstrings to 72 characters.&lt;/li&gt;
&lt;li&gt;Use complete sentences, starting with a capital letter.&lt;/li&gt;
&lt;li&gt;Make sure to update comments if you change your code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;block-comments&quot;&gt;Block Comments&lt;/h3&gt;
&lt;p&gt;Use block comments to document a small section of code. They are useful when you have to write several lines of code to perform a single action, such as importing data from a file or updating a database entry. They are important as they help others understand the purpose and functionality of a given code block.&lt;/p&gt;
&lt;p&gt;PEP 8 provides the following rules for writing block comments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indent block comments to the same level as the code they describe.&lt;/li&gt;
&lt;li&gt;Start each line with a &lt;code&gt;#&lt;/code&gt; followed by a single space.&lt;/li&gt;
&lt;li&gt;Separate paragraphs by a line containing a single &lt;code&gt;#&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is a block comment explaining the function of a &lt;code&gt;for&lt;/code&gt; loop. Note that the sentence wraps to a new line to preserve the 79 character line limit:&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;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;0&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;c1&quot;&gt;# Loop over i ten times and print out the value of i, followed by a&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# new line character&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;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sometimes, if the code is very technical, then it is necessary to use more than one paragraph in a block comment:&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;quadratic&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&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;c1&quot;&gt;# Calculate the solution to a quadratic equation using the quadratic&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# formula.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# There are always two solutions to a quadratic equation, x_1 and x_2.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x_1&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;n&quot;&gt;b&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;b&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;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&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;c&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;mi&quot;&gt;1&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;o&quot;&gt;/&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;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x_2&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;n&quot;&gt;b&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;b&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;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&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;c&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;mi&quot;&gt;1&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;o&quot;&gt;/&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;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&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;x_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x_2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you&amp;rsquo;re ever in doubt as to what comment type is suitable, then block comments are often the way to go. Use them as much as possible throughout your code, but make sure to update them if you make changes to your code!&lt;/p&gt;
&lt;h3 id=&quot;inline-comments&quot;&gt;Inline Comments&lt;/h3&gt;
&lt;p&gt;Inline comments explain a single statement in a piece of code. They are useful to remind you, or explain to others, why a certain line of code is necessary. Here&amp;rsquo;s what PEP 8 has to say about them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use inline comments sparingly.&lt;/li&gt;
&lt;li&gt;Write inline comments on the same line as the statement they refer to.&lt;/li&gt;
&lt;li&gt;Separate inline comments by two or more spaces from the statement.&lt;/li&gt;
&lt;li&gt;Start inline comments with a &lt;code&gt;#&lt;/code&gt; and a single space, like block comments.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t use them to explain the obvious.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below is an example of an inline comment:&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;5&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This is an inline comment&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sometimes, inline comments can seem necessary, but you can use better naming conventions instead. Here&amp;rsquo;s an 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;s1&quot;&gt;&amp;#39;John Smith&amp;#39;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Student Name&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, the inline comment does give extra information. However using &lt;code&gt;x&lt;/code&gt; as a variable name for a person&amp;rsquo;s name is bad practice. There&amp;rsquo;s no need for the inline comment if you rename your variable:&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;student_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;John Smith&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, inline comments such as these are bad practice as they state the obvious and clutter 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;n&quot;&gt;empty_list&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;c1&quot;&gt;# Initialize empty list&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;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;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Multiply x by 5&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Inline comments are more specific than block comments, and it&amp;rsquo;s easy to add them when they&amp;rsquo;re not necessary, which leads to clutter. You could get away with only using block comments so, unless you are sure you need an inline comment, your code is more likely to be PEP 8 compliant if you stick to block comments.&lt;/p&gt;
&lt;h3 id=&quot;documentation-strings&quot;&gt;Documentation Strings&lt;/h3&gt;
&lt;p&gt;Documentation strings, or docstrings, are strings enclosed in double (&lt;code&gt;&quot;&quot;&quot;&lt;/code&gt;) or single (&lt;code&gt;&#39;&#39;&#39;&lt;/code&gt;) quotation marks that appear on the first line of any function, class, method, or module. You can use them to explain and document a specific block of code. There is an entire PEP, &lt;a href=&quot;https://www.python.org/dev/peps/pep-0257/&quot;&gt;PEP 257&lt;/a&gt;, that covers docstrings, but you&amp;rsquo;ll get a summary in this section.&lt;/p&gt;
&lt;p&gt;The most important rules applying to docstrings are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Surround docstrings with three double quotes on either side, as in &lt;code&gt;&quot;&quot;&quot;This is a docstring&quot;&quot;&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Write them for all public modules, functions, classes, and methods.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Put the &lt;code&gt;&quot;&quot;&quot;&lt;/code&gt; that ends a multiline docstring on a line by itself:&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;quadratic&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Solve quadratic equation via the quadratic formula.&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;    A quadratic equation has the following form:&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    ax**2 + bx + c = 0&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;    There always two solutions to a quadratic equation: x_1 &amp;amp; x_2.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x_1&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;n&quot;&gt;b&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;b&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;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&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;c&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;mi&quot;&gt;1&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;o&quot;&gt;/&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;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x_2&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;n&quot;&gt;b&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;b&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;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&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;c&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;mi&quot;&gt;1&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;o&quot;&gt;/&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;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&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;x_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x_2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For one-line docstrings, keep the &lt;code&gt;&quot;&quot;&quot;&lt;/code&gt; on the same 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;quadratic&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Use the quadratic formula&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x_1&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;n&quot;&gt;b&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;b&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;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&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;c&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;mi&quot;&gt;1&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;o&quot;&gt;/&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;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x_2&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;n&quot;&gt;b&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;b&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;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&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;c&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;mi&quot;&gt;1&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;o&quot;&gt;/&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;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&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;x_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x_2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a more detailed article on documenting Python code, see &lt;a href=&quot;https://realpython.com/documenting-python-code/#docstrings-background&quot;&gt;Documenting Python Code: A Complete Guide&lt;/a&gt; by James Mertz.&lt;/p&gt;
&lt;h2 id=&quot;whitespace-in-expressions-and-statements&quot;&gt;Whitespace in Expressions and Statements&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Sparse is better than dense.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; &lt;em&gt;The Zen of Python&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Whitespace can be very helpful in expressions and statements when used properly. If there is not enough whitespace, then code can be difficult to read, as it&amp;rsquo;s all bunched together. If there&amp;rsquo;s too much whitespace, then it can be difficult to visually combine related terms in a statement.&lt;/p&gt;
&lt;h3 id=&quot;whitespace-around-binary-operators&quot;&gt;Whitespace Around Binary Operators&lt;/h3&gt;
&lt;p&gt;Surround the following binary operators with a single space on either side:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Assignment operators (&lt;code&gt;=&lt;/code&gt;, &lt;code&gt;+=&lt;/code&gt;, &lt;code&gt;-=&lt;/code&gt;, and so forth)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Comparisons (&lt;code&gt;==&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;. &lt;code&gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;) and (&lt;code&gt;is&lt;/code&gt;, &lt;code&gt;is not&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt;, &lt;code&gt;not in&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Booleans (&lt;code&gt;and&lt;/code&gt;, &lt;code&gt;not&lt;/code&gt;, &lt;code&gt;or&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: When &lt;code&gt;=&lt;/code&gt; is used to assign a default value to a function argument, do not surround it with spaces.&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;# Recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default_parameter&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;# ...&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;# Not recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default_parameter&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;# ...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;When there&amp;rsquo;s more than one operator in a statement, adding a single space before and after each operator can look confusing. Instead, it is better to only add whitespace around the operators with the lowest priority, especially when performing mathematical manipulation. Here are a couple examples:&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;# Recommended&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;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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;z&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;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;o&quot;&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;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;c1&quot;&gt;# Not Recommended&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;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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;z&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;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;o&quot;&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;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;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also apply this to &lt;code&gt;if&lt;/code&gt; statements where there are multiple conditions:&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;# Not recommended&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;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&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;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;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;x is larger than 5 and divisible by 2!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the above example, the &lt;code&gt;and&lt;/code&gt; operator has lowest priority. It may therefore be clearer to express the &lt;code&gt;if&lt;/code&gt; statement as 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;c1&quot;&gt;# Recommended&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;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&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;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;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;x is larger than 5 and divisible by 2!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You are free to choose which is clearer, with the caveat that you must use the same amount of whitespace either side of the operator.&lt;/p&gt;
&lt;p&gt;The following is not acceptable:&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;# Definitely do not do this!&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;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&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;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;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;x is larger than 5 and divisible by 2!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In slices, colons act as a binary operators.
Therefore, the rules outlined in the previous section apply, and there should be the same amount of whitespace either side. The following examples of list slices are valid:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&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;mi&quot;&gt;3&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;c1&quot;&gt;# Treat the colon as the operator with lowest priority&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;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;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;c1&quot;&gt;# In an extended slice, both colons must be&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# surrounded by the same amount of whitespace&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;mi&quot;&gt;3&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;5&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;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;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;n&quot;&gt;x&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;c1&quot;&gt;# The space is omitted if a slice parameter is omitted&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;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;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;/pre&gt;&lt;/div&gt;

&lt;p&gt;In summary, you should surround most operators with whitespace. However, there are some caveats to this rule, such as in function arguments or when you&amp;rsquo;re combining multiple operators in one statement.&lt;/p&gt;
&lt;h3 id=&quot;when-to-avoid-adding-whitespace&quot;&gt;When to Avoid Adding Whitespace&lt;/h3&gt;
&lt;p&gt;In some cases, adding whitespace can make code harder to read. Too much whitespace can make code overly sparse and difficult to follow. PEP 8 outlines very clear examples where whitespace is inappropriate.&lt;/p&gt;
&lt;p&gt;The most important place to avoid adding whitespace is at the end of a line. This is known as &lt;strong&gt;trailing whitespace&lt;/strong&gt;. It is invisible and can produce errors that are difficult to trace.&lt;/p&gt;
&lt;p&gt;The following list outlines some cases where you should avoid adding whitespace:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Immediately inside parentheses, brackets, or braces: &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;# Recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my_list&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;mi&quot;&gt;2&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;c1&quot;&gt;# Not recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my_list&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;mi&quot;&gt;2&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;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Before a comma, semicolon, or colon:&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;5&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;mi&quot;&gt;6&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Recommended&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;x&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;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Not recommended&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;x&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;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Before the open parenthesis that starts the argument list of a function 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;double&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;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;mi&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;double&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;c1&quot;&gt;# Not recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;double&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;/li&gt;
&lt;li&gt;
&lt;p&gt;Before the open bracket that starts an index or slice: &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;# Recommended&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;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Not recommended&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;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Between a trailing comma and a closing parenthesis: &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;# Recommended&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;tuple&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;c1&quot;&gt;# Not recommended&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;tuple&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;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To align assignment operators:&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;# Recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;var1&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;n&quot;&gt;var2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;some_long_var&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Not recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;var1&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;n&quot;&gt;var2&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;some_long_var&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure that there is no trailing whitespace anywhere in your code. There are other cases where PEP 8 discourages adding extra whitespace, such as immediately inside brackets, as well as before commas and colons. You should also never add extra whitespace in order to align operators.&lt;/p&gt;
&lt;h2 id=&quot;programming-recommendations&quot;&gt;Programming Recommendations&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Simple is better than complex.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; &lt;em&gt;The Zen of Python&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You will often find that there are several ways to perform a similar action in Python (and any other programming language for that matter). In this section, you&amp;rsquo;ll see some of the suggestions PEP 8 provides to remove that ambiguity and preserve consistency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don&amp;rsquo;t compare boolean values to &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt; using the equivalence operator.&lt;/strong&gt; You&amp;rsquo;ll often need to check if a boolean value is True or False. When doing so, it is intuitive to do this with a statement like the one 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;c1&quot;&gt;# Not recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my_bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_bool&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;return&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;6 is bigger than 5&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The use of the equivalence operator, &lt;code&gt;==&lt;/code&gt;, is unnecessary here. &lt;code&gt;bool&lt;/code&gt; can only take values &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;. It is enough to write 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;c1&quot;&gt;# Recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_bool&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;s1&quot;&gt;&amp;#39;6 is bigger than 5&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way of performing an &lt;code&gt;if&lt;/code&gt; statement with a boolean requires less code and is simpler, so PEP 8 encourages it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use the fact that empty sequences are falsy in &lt;code&gt;if&lt;/code&gt; statements.&lt;/strong&gt; If you want to check whether a list is empty, you might be tempted to check the length of the list. If the list is empty, it&amp;rsquo;s length is &lt;code&gt;0&lt;/code&gt; which is equivalent to &lt;code&gt;False&lt;/code&gt; when used in an &lt;code&gt;if&lt;/code&gt; statement. Here&amp;rsquo;s an 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;# Not recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my_list&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;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&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;my_list&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;List is empty!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, in Python any empty list, string, or tuple is &lt;a href=&quot;https://docs.python.org/3/library/stdtypes.html#truth-value-testing&quot;&gt;falsy&lt;/a&gt;. We can therefore come up with a simpler alternative to the above:&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;# Recommended&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my_list&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;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_list&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;List is empty!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While both examples will print out &lt;code&gt;List is empty!&lt;/code&gt;, the second option is simpler, so PEP 8 encourages it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;is not&lt;/code&gt; rather than &lt;code&gt;not ... is&lt;/code&gt; in &lt;code&gt;if&lt;/code&gt; statements.&lt;/strong&gt; If you are trying to check whether a variable has a defined value, there are two options. The first is to evaluate an &lt;code&gt;if&lt;/code&gt; statement with &lt;code&gt;x is not None&lt;/code&gt;, as in the example 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;c1&quot;&gt;# Recommended&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;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;x exists!&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A second option would be to evaluate &lt;code&gt;x is None&lt;/code&gt; and then have an &lt;code&gt;if&lt;/code&gt; statement based on &lt;code&gt;not&lt;/code&gt; the outcome:&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;# Not recommended&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;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;x exists!&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While both options will be evaluated correctly, the first is simpler, so PEP 8 encourages it. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don&amp;rsquo;t use &lt;code&gt;if x:&lt;/code&gt; when you mean &lt;code&gt;if x is not None:&lt;/code&gt;.&lt;/strong&gt; Sometimes, you may have a function with arguments that are &lt;code&gt;None&lt;/code&gt; by default. A common mistake when checking if such an argument, &lt;code&gt;arg&lt;/code&gt;, has been given a different value is to use 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;c1&quot;&gt;# Not Recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Do something with arg...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code checks that &lt;code&gt;arg&lt;/code&gt; is truthy. Instead, you want to check that &lt;code&gt;arg&lt;/code&gt; is &lt;code&gt;not None&lt;/code&gt;, so it would be better to use 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;c1&quot;&gt;# Recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg&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;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Do something with arg...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The mistake being made here is assuming that &lt;code&gt;not None&lt;/code&gt; and truthy are equivalent. You could have set &lt;code&gt;arg = []&lt;/code&gt;. As we saw above, empty lists are evaluated as falsy in Python. So, even though the argument &lt;code&gt;arg&lt;/code&gt; has been assigned, the condition is not met, and so the code in the body of the &lt;code&gt;if&lt;/code&gt; statement will not be executed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;.startswith()&lt;/code&gt; and &lt;code&gt;.endswith()&lt;/code&gt; instead of slicing.&lt;/strong&gt; If you were trying to check if a string &lt;code&gt;word&lt;/code&gt; was prefixed, or suffixed, with the word &lt;code&gt;cat&lt;/code&gt;, it might seem sensible to use &lt;a href=&quot;https://realpython.com/python-strings/#string-slicing&quot;&gt;list slicing&lt;/a&gt;. However, list slicing is prone to error, and you have to hardcode the number of characters in the prefix or suffix. It is also not clear to someone less familiar with Python list slicing what you are trying to achieve:&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;# Not recommended&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;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;o&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;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;The word starts with &amp;quot;cat&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;However, this is not as readable as using &lt;code&gt;.startswith()&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;c1&quot;&gt;# Recommended&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startswith&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;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;The word starts with &amp;quot;cat&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;Similarly, the same principle applies when you&amp;rsquo;re checking for suffixes. The example below outlines how you might check whether a string ends in &lt;code&gt;jpg&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;c1&quot;&gt;# Not recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_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;mi&quot;&gt;3&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;jpg&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;The file is a JPEG&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While the outcome is correct, the notation is a bit clunky and hard to read. Instead, you could use &lt;code&gt;.endswith()&lt;/code&gt; as in the example 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;c1&quot;&gt;# Recommended&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endswith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;jpg&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;The file is a JPEG&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As with most of these programming recommendations, the goal is readability and simplicity. In Python, there are many different ways to perform the same action, so guidelines on which methods to chose are helpful.&lt;/p&gt;
&lt;h2 id=&quot;when-to-ignore-pep-8&quot;&gt;When to Ignore PEP 8&lt;/h2&gt;
&lt;p&gt;The short answer to this question is never. If you follow PEP 8 to the letter, you can guarantee that you&amp;rsquo;ll have clean, professional, and readable code. This will benefit you as well as collaborators and potential employers.&lt;/p&gt;
&lt;p&gt;However, some guidelines in PEP 8 are inconvenient in the following instances:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If complying with PEP 8 would break compatibility with existing software&lt;/li&gt;
&lt;li&gt;If code surrounding what you&amp;rsquo;re working on is inconsistent with PEP 8&lt;/li&gt;
&lt;li&gt;If code needs to remain compatible with older versions of Python&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tips-and-tricks-to-help-ensure-your-code-follows-pep-8&quot;&gt;Tips and Tricks to Help Ensure Your Code Follows PEP 8&lt;/h2&gt;
&lt;p&gt;There is a lot to remember to make sure your code is PEP 8 compliant. It can be a tall order to remember all these rules when you&amp;rsquo;re developing code. It&amp;rsquo;s particularly time consuming to update past projects to be PEP 8 compliant. Luckily, there are tools that can help speed up this process. There are two classes of tools that you can use to enforce PEP 8 compliance: linters and autoformatters.&lt;/p&gt;
&lt;h3 id=&quot;linters&quot;&gt;Linters&lt;/h3&gt;
&lt;p&gt;Linters are programs that analyze code and flag errors. They provide suggestions on how to fix the error. Linters are particularly useful when installed as extensions to your text editor, as they flag errors and stylistic problems while you write. In this section, you&amp;rsquo;ll see an outline of how the linters work, with links to the text editor extensions at the end.&lt;/p&gt;
&lt;p&gt;The best linters for Python code are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://pypi.org/project/pycodestyle/&quot;&gt;&lt;code&gt;pycodestyle&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; is a tool to check your Python code against some of the style conventions in PEP 8.&lt;/p&gt;
&lt;p&gt;Install &lt;code&gt;pycodestyle&lt;/code&gt; using &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 pycodestyle
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can run &lt;code&gt;pycodestyle&lt;/code&gt; from the terminal using 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; pycodestyle code.py
&lt;span class=&quot;go&quot;&gt;code.py:1:17: E231 missing whitespace after &amp;#39;,&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;code.py:2:21: E231 missing whitespace after &amp;#39;,&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;code.py:6:19: E711 comparison to None should be &amp;#39;if cond is None:&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://pypi.org/project/flake8/&quot;&gt;&lt;code&gt;flake8&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; is a tool that combines a debugger, &lt;code&gt;pyflakes&lt;/code&gt;, with &lt;code&gt;pycodestyle&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Install &lt;code&gt;flake8&lt;/code&gt; using &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 flake8
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;flake8&lt;/code&gt; from the terminal using 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; flake8 code.py
&lt;span class=&quot;go&quot;&gt;code.py:1:17: E231 missing whitespace after &amp;#39;,&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;code.py:2:21: E231 missing whitespace after &amp;#39;,&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;code.py:3:17: E999 SyntaxError: invalid syntax&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;code.py:6:19: E711 comparison to None should be &amp;#39;if cond is None:&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;An example of the output is also shown.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&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 extra line of output indicates a syntax error.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;These are also available as extensions for &lt;a href=&quot;https://atom.io/packages/linter-flake8&quot;&gt;Atom&lt;/a&gt;, &lt;a href=&quot;https://github.com/SublimeLinter/SublimeLinter-flake8&quot;&gt;Sublime Text&lt;/a&gt;, &lt;a href=&quot;https://code.visualstudio.com/docs/python/linting#_flake8&quot;&gt;Visual Studio Code&lt;/a&gt;, and &lt;a href=&quot;https://github.com/nvie/vim-flake8&quot;&gt;VIM&lt;/a&gt;. You can also find guides on setting up &lt;a href=&quot;https://realpython.com/setting-up-sublime-text-3-for-full-stack-python-development/&quot;&gt;Sublime Text&lt;/a&gt; and &lt;a href=&quot;https://realpython.com/vim-and-python-a-match-made-in-heaven/#code-folding&quot;&gt;VIM&lt;/a&gt; for Python development, as well as an &lt;a href=&quot;https://realpython.com/python-ides-code-editors-guide/&quot;&gt;overview of some popular text editors&lt;/a&gt; at &lt;em&gt;Real Python&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;autoformatters&quot;&gt;Autoformatters&lt;/h3&gt;
&lt;p&gt;Autoformatters are programs that refactor your code to conform with PEP 8 automatically. Once such program is &lt;a href=&quot;https://pypi.org/project/black/&quot;&gt;&lt;code&gt;black&lt;/code&gt;&lt;/a&gt;, which autoformats code following &lt;em&gt;most&lt;/em&gt; of the rules in PEP 8. One big difference is that it limits line length to 88 characters, rather than 79. However, you can overwrite this by adding a command line flag, as you&amp;rsquo;ll see in an example below. &lt;/p&gt;
&lt;p&gt;Install &lt;code&gt;black&lt;/code&gt; using &lt;code&gt;pip&lt;/code&gt;. It requires Python 3.6+ to run:&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 black
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It can be run via the command line, as with the linters. Let&amp;rsquo;s say you start with the following code that isn&amp;rsquo;t PEP 8 compliant in a file called &lt;code&gt;code.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;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;0&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&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;0&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;k&quot;&gt;if&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;mi&quot;&gt;2&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;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can then run the following command via the command line:&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; black code.py
&lt;span class=&quot;go&quot;&gt;reformatted code.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;All done! ✨ 🍰 ✨&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;code.py&lt;/code&gt; will be automatically reformatted to look 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;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;0&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&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;0&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;k&quot;&gt;if&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;mi&quot;&gt;2&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;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you want to alter the line length limit, then you can use the &lt;code&gt;--line-length&lt;/code&gt; flag:&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; black --line-length&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;79&lt;/span&gt; code.py
&lt;span class=&quot;go&quot;&gt;reformatted code.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;All done! ✨ 🍰 ✨&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Two other autoformatters, &lt;a href=&quot;https://pypi.org/project/autopep8/&quot;&gt;&lt;code&gt;autopep8&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://pypi.org/project/yapf/&quot;&gt;&lt;code&gt;yapf&lt;/code&gt;&lt;/a&gt;, perform actions that are similar to what &lt;code&gt;black&lt;/code&gt; does.&lt;/p&gt;
&lt;p&gt;Another &lt;em&gt;Real Python&lt;/em&gt; tutorial, &lt;a href=&quot;https://realpython.com/python-code-quality/&quot;&gt;Python Code Quality: Tools &amp;amp; Best Practices&lt;/a&gt; by Alexander van Tol, gives a thorough explanation of how to use these tools.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You now know how to write high-quality, readable Python code by using the guidelines laid out in PEP 8. While the guidelines can seem pedantic, following them can really improve your code, especially when it comes to sharing your code with potential employers or collaborators.&lt;/p&gt;
&lt;p&gt;In this tutorial, you learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What PEP 8 is and why it exists&lt;/li&gt;
&lt;li&gt;Why you should aim to write PEP 8 compliant code&lt;/li&gt;
&lt;li&gt;How to write code that is PEP 8 compliant&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On top of all this, you also saw how to use linters and autoformatters to check your code against PEP 8 guidelines. &lt;/p&gt;
&lt;p&gt;If you want to learn more about PEP 8, then you can read the &lt;a href=&quot;https://www.python.org/dev/peps/pep-0008/&quot;&gt;full documentation&lt;/a&gt;, or visit &lt;a href=&quot;https://pep8.org&quot;&gt;pep8.org&lt;/a&gt;, which contains the same information but has been nicely formatted. In these documents, you will find the rest of the PEP 8 guidelines not covered in this tutorial.&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>A Pythonista&#39;s Holiday Wish List</title>
      <id>https://realpython.com/python-holiday-wish-list/</id>
      <link href="https://realpython.com/python-holiday-wish-list/"/>
      <updated>2018-12-17T14:00:00+00:00</updated>
      <summary>Whether you’re a friend of a Python developer or one yourself, we&#39;ve got the perfect wish list if you’re looking to get a little something special. James Mertz combed through the interwebs and polled his fellow Real Python authors to find the best presents and gifts for Pythonistas.</summary>
      <content type="html">
        &lt;p&gt;It&amp;rsquo;s that time of year again when everyone is looking to get last minute gifts. Whether you&amp;rsquo;re a friend of a Python developer or one yourself, I&amp;rsquo;ve got the perfect wish list if you&amp;rsquo;re looking to get a little something special. I&amp;rsquo;ve combed through the interwebs and polled my fellow Real Python authors to find the best presents for Pythonistas.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve broken up these suggestions into five categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Good causes&lt;/li&gt;
&lt;li&gt;Learning resources&lt;/li&gt;
&lt;li&gt;Python swag&lt;/li&gt;
&lt;li&gt;Hardware&lt;/li&gt;
&lt;li&gt;Gadgets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;donating-to-a-good-cause&quot;&gt;Donating to a Good Cause&lt;/h2&gt;
&lt;p&gt;The greatest gift that anyone can receive is the feeling of giving to someone else. Whether you want to make a donation yourself or make one in someone&amp;rsquo;s name, here are some great Python related organizations that could use a donation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.python.org/psf/donations/&quot;&gt;Python Software Foundation (PSF)&lt;/a&gt;: the official organization that maintains Python and hosts the Python Conference each year&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.djangoproject.com/foundation/&quot;&gt;Django Software Foundation (DSF)&lt;/a&gt;: the official organization that maintains Django, our favorite web app framework&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.pyladies.com/sponsor/&quot;&gt;PyLadies&lt;/a&gt;: an organization dedicated to helping more women become participants and leaders in the Python open-source community&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donating your money isn&amp;rsquo;t the only thing that you can do to help the Python Community. You can also donate your time &lt;a href=&quot;https://www.python.org/psf/volunteer/&quot;&gt;as a volunteer&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;python-learning-resources&quot;&gt;Python Learning Resources&lt;/h2&gt;
&lt;p&gt;I think that one of the greatest gifts that can be given is knowledge. Want to get a good read that&amp;rsquo;s Python related? Check out &lt;a href=&quot;https://realpython.com/best-python-books/&quot;&gt;The Best Python Books&lt;/a&gt; for a great collection of Python related books.  Maybe you want something a little more interactive? Check out &lt;a href=&quot;https://realpython.com/products/&quot;&gt;the courses at &lt;em&gt;Real Python&lt;/em&gt;&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;One of the best ways to learn about Python is to learn from the masters themselves. A &lt;a href=&quot;https://us.pycon.org/2019/&quot;&gt;ticket to PyCon 2019&lt;/a&gt; would be the greatest learning gift of all: three days of classes, demonstrations, coding challenges, and rubbing shoulders with the people who dream in Python code! This would be the ultimate gift that would keep on giving. You&amp;rsquo;ll also get swag bags that will last you all year round.&lt;/p&gt;
&lt;h2 id=&quot;python-stickers-t-shirts-and-coffee-mugs-oh-my&quot;&gt;Python Stickers, T-Shirts, and Coffee Mugs&amp;hellip; Oh My!&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;re Pythonistas, so why not show off the programming language we love most! I love showing off my love for Python on my laptop.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/IMG_2229.1491b707707e.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/IMG_2229.1491b707707e.JPG&quot; width=&quot;4032&quot; height=&quot;3024&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/IMG_2229.1491b707707e.JPG&amp;amp;w=1008&amp;amp;sig=03c37fa7b53c19fc8fb94141bc6f1b08417a1bf0 1008w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/IMG_2229.1491b707707e.JPG&amp;amp;w=2016&amp;amp;sig=29c00f94174c13a401c830ad32c41b63cf61652f 2016w, https://files.realpython.com/media/IMG_2229.1491b707707e.JPG 4032w&quot; sizes=&quot;75vw&quot; alt=&quot;My Personal Laptop Sporting Python Stickers&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nerdlettering.com&quot;&gt;Nerdlettering&lt;/a&gt; has got your back for all your sticker needs, and if stickers aren&amp;rsquo;t your thing, then perhaps a sweatshirt or t-shirt might be better.  Also don&amp;rsquo;t forget about that old coffee mug of yours. It could probably use an upgrade as well.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nerdlettering.com/collections/mugs-for-python-developers/products/from-coffee-import-python-mug-black&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-33&quot; src=&quot;https://files.realpython.com/media/ZJRFVjw8BAhfYrCXeD4atYTJRxHZ7M-right_1024x1024_a19fde86-aee6-4188-9857-b211933f4f24_530x530.5868ff89bfd9.png&quot; width=&quot;530&quot; height=&quot;530&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ZJRFVjw8BAhfYrCXeD4atYTJRxHZ7M-right_1024x1024_a19fde86-aee6-4188-9857-b211933f4f24_530x530.5868ff89bfd9.png&amp;amp;w=132&amp;amp;sig=c08193746c8a90137f8bafbc246c76603805152d 132w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ZJRFVjw8BAhfYrCXeD4atYTJRxHZ7M-right_1024x1024_a19fde86-aee6-4188-9857-b211933f4f24_530x530.5868ff89bfd9.png&amp;amp;w=265&amp;amp;sig=0589d3611c870b715ba536eaa899307dd03ec2e3 265w, https://files.realpython.com/media/ZJRFVjw8BAhfYrCXeD4atYTJRxHZ7M-right_1024x1024_a19fde86-aee6-4188-9857-b211933f4f24_530x530.5868ff89bfd9.png 530w&quot; sizes=&quot;75vw&quot; alt=&quot;&amp;quot;from coffee import *&amp;quot; Python Coffee Mug&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Want something that sports the Real Python logo? &lt;a href=&quot;https://realpython.com/products/merch/&quot;&gt;They&amp;rsquo;ve got you covered as well&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;hardware&quot;&gt;Hardware&lt;/h2&gt;
&lt;p&gt;With the way that tech is advancing every day, every programmer needs an upgrade of their hardware. This may be because you&amp;rsquo;re running out of hard drive space, have worn out keyboards, or just want something fun. (Or maybe you want something more advanced. Everyone loves a good challenge right?) This is my recommended hardware to check out this season.&lt;/p&gt;
&lt;h3 id=&quot;external-hdd&quot;&gt;External HDD&lt;/h3&gt;
&lt;p&gt;Whether you have a massive code base or are running several hundred Docker images, you can quickly run out of space to do all your work. Good thing the cost of hard drive capacity is almost constantly dropping! Plus, with USB 3.0, it&amp;rsquo;s almost as fast or even faster than an internal hard drive. You can get &lt;a href=&quot;https://realpython.com/asins/B00FRHTTIA&quot;&gt;2 TB of portable space for less than $60&lt;/a&gt;! Need something bigger? For &lt;a href=&quot;https://realpython.com/asins/B01HAPGEIE&quot;&gt;a little more than double the price, you can get 8 TB&lt;/a&gt;! I recommend sticking to the name brands like Seagate and Western Digital if you can.&lt;/p&gt;
&lt;h3 id=&quot;a-new-mechanical-keyboard&quot;&gt;A New Mechanical Keyboard&lt;/h3&gt;
&lt;p&gt;As developers, we&amp;rsquo;re using the keyboard 99% of the time we&amp;rsquo;re on the computer. That means that it&amp;rsquo;s important to have the right keyboard that feels right for you. Don&amp;rsquo;t know what to get? The folks over at the &lt;a href=&quot;https://www.reddit.com/r/MechanicalKeyboards&quot;&gt;MechanicalKeyboards subreddit&lt;/a&gt; have created &lt;a href=&quot;https://www.reddit.com/r/MechanicalKeyboards/wiki/buying_guide&quot;&gt;a great guide&lt;/a&gt; to all things mechanical keyboards.&lt;/p&gt;
&lt;p&gt;Recommending a keyboard is like recommending underwear: you never know how it&amp;rsquo;s going to feel for anyone else, so I highly recommend that you look at the guide above. But if you want my opinion, the keyboard that I use and think feels the best is the &lt;a href=&quot;https://realpython.com/asins/B00CYX54C0&quot;&gt;Microsoft Sculpt Ergo Keyboard&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://realpython.com/asins/B00CYX54C0&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-33&quot; src=&quot;https://files.realpython.com/media/51ujzkGdJQL._SL1000_.f774e66daa3f.7a5e3fa22b48.jpg&quot; width=&quot;896&quot; height=&quot;296&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/51ujzkGdJQL._SL1000_.f774e66daa3f.7a5e3fa22b48.jpg&amp;amp;w=224&amp;amp;sig=e9193c450dc94da31e3e6af9c7da9e287b1952b8 224w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/51ujzkGdJQL._SL1000_.f774e66daa3f.7a5e3fa22b48.jpg&amp;amp;w=448&amp;amp;sig=0d309d0af9652aa1701b671057705317a9976050 448w, https://files.realpython.com/media/51ujzkGdJQL._SL1000_.f774e66daa3f.7a5e3fa22b48.jpg 896w&quot; sizes=&quot;75vw&quot; alt=&quot;Microsoft Sculpt Keyboard&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;headphones&quot;&gt;Headphones&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s face it: there&amp;rsquo;s nothing like getting into the zone with your favorite music set and crunching away at your codebase. So why not make sure that you get a good set of headphones? I love my &lt;a href=&quot;https://realpython.com/asins/B01ERLN180&quot;&gt;Beyerdynamic DT 770 Pro&lt;/a&gt; headphones and can&amp;rsquo;t imagine coding without them. When you&amp;rsquo;re buying headphones, I recommend using &lt;a href=&quot;https://sites.google.com/view/quipa/assistants&quot;&gt;this useful guide&lt;/a&gt; created by the helpful folks over at the &lt;a href=&quot;https://www.reddit.com/r/headphones&quot;&gt;headphones subreddit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://realpython.com/asins/B01ERLN180&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-33&quot; src=&quot;https://files.realpython.com/media/81hXf-JgMLL._SL1500_.b85e6c69cf33.jpg&quot; width=&quot;1500&quot; height=&quot;1358&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/81hXf-JgMLL._SL1500_.b85e6c69cf33.jpg&amp;amp;w=375&amp;amp;sig=8dcbe642508f154fc25703e7dcc3df31c7fa1232 375w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/81hXf-JgMLL._SL1500_.b85e6c69cf33.jpg&amp;amp;w=750&amp;amp;sig=31af5b908e2a862f6ac4b9ec6cafa653d6cbb9a0 750w, https://files.realpython.com/media/81hXf-JgMLL._SL1500_.b85e6c69cf33.jpg 1500w&quot; sizes=&quot;75vw&quot; alt=&quot;Beyerdynamic DT-770 Pro 80 LE Black&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;gadgets&quot;&gt;Gadgets&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s always fun to have new toys to play with, and us Python developers have a lot to choose from. Here, we&amp;rsquo;ve collected our favorite gadgets that use Python.&lt;/p&gt;
&lt;h3 id=&quot;raspberry-pi&quot;&gt;Raspberry Pi&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s start off with a little pie&amp;mdash;Raspberry Pi that is. There are so many things that you can do with a Raspberry Pi, including the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://spin.atomicobject.com/2014/06/28/raspberry-pi-gardening/&quot;&gt;Automating Your Garden&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lifehacker.com/build-an-entire-home-automation-system-with-a-raspberry-1640844965&quot;&gt;Automating Your Home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.instructables.com/id/Build-your-own-Mini-Arcade-Cabinet-with-Raspberry-/&quot;&gt;Building an Old-School Gaming Arcade&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.raspberrypi.org/magpi/cluster-computer-raspberry-pi-3/&quot;&gt;Cluster Computing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://projects.raspberrypi.org/en/&quot;&gt;So much more!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With its starting price of only $35, it&amp;rsquo;s amazing that you&amp;rsquo;re getting a multi-core, Bluetooth and WiFi enabled device! I highly recommend getting a kit to get started.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.raspberrypi.org&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-33&quot; src=&quot;https://files.realpython.com/media/770A5614-1617x1080.7f696e9547e9.jpg&quot; width=&quot;1617&quot; height=&quot;1080&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/770A5614-1617x1080.7f696e9547e9.jpg&amp;amp;w=404&amp;amp;sig=040b7f4d0e4f063da58f66ae8c0ea0d3905855fa 404w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/770A5614-1617x1080.7f696e9547e9.jpg&amp;amp;w=808&amp;amp;sig=820ffd0b880967bc17a4afaeeed63d20ced133c7 808w, https://files.realpython.com/media/770A5614-1617x1080.7f696e9547e9.jpg 1617w&quot; sizes=&quot;75vw&quot; alt=&quot;The Raspberry Pi Logo&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.raspberrypi.org/products/&quot;&gt;Get the Raspberry Pi from their site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/asins/B07BCC8PK7&quot;&gt;Get the Raspberry Pi 3 CanaKit on Amazon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-open-source-rover&quot;&gt;The Open Source Rover&lt;/h3&gt;
&lt;p&gt;Speaking of Raspberry Pi, how about creating your own Mars Science Lab! NASA&amp;rsquo;s Jet Propulsion Lab (JPL), the creators of the Mars Rovers, has created an &lt;a href=&quot;https://opensourcerover.jpl.nasa.gov/&quot;&gt;Open Source replica of the Mars Science Laboratory&lt;/a&gt; rover that uses the Raspberry Pi.&lt;/p&gt;
&lt;p&gt;This comes with a hefty price tag (~$2,500) and isn&amp;rsquo;t for the faint of heart as it requires purchasing each part separately and assembling the entire rover. Don&amp;rsquo;t worry about programming the rover though. That was already done by JPL in Python! If the price and difficulty don&amp;rsquo;t scare you off, then this is a great gift for anyone wanting to learn about robotics.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/rover.dba7f28d2c7c.png&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/rover.dba7f28d2c7c.png&quot; width=&quot;1290&quot; height=&quot;1099&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/rover.dba7f28d2c7c.png&amp;amp;w=322&amp;amp;sig=49d04a9a91b11df66782c9c6f37b98c41a42507a 322w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/rover.dba7f28d2c7c.png&amp;amp;w=645&amp;amp;sig=4b15c674626f3135ba53d6e2e0bb7ab9e61901a7 645w, https://files.realpython.com/media/rover.dba7f28d2c7c.png 1290w&quot; sizes=&quot;75vw&quot; alt=&quot;NASA&amp;#39;s JPL Open Source Rover&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nasa-jpl/open-source-rover&quot;&gt;Github Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nasa-jpl/open-source-rover/raw/master/osr_Master_parts_list.xlsx&quot;&gt;Parts List&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nasa-jpl/osr-rover-code&quot;&gt;Python Source Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;vector-and-cozmo-robots-from-anki&quot;&gt;Vector and Cozmo Robots From Anki&lt;/h3&gt;
&lt;p&gt;If making your own robot seems a little daunting, then check out Vector and Cozmo. Vector is &amp;ldquo;the good robot&amp;rdquo; who can help out with multiple things such as a kitchen timer, taking a selfie, or telling you what the weather is going to be like. If that&amp;rsquo;s not enough, there is the &lt;a href=&quot;https://developer.anki.com/blog/news/the-vector-sdk-hits-alpha/&quot;&gt;Python Software Development Kit (SDK)&lt;/a&gt; that enables you to make Vector fully customizable!&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re new to programming or want to teach someone about programming robots, then Cozmo&amp;rsquo;s got you covered. With both a full Python SDK and a more simple option available, this robot is a good choice for the beginner and the seasoned coder.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/anki-vector-robot.9c39f6f21ffc.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-33&quot; src=&quot;https://files.realpython.com/media/anki-vector-robot.9c39f6f21ffc.jpg&quot; width=&quot;2336&quot; height=&quot;1064&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/anki-vector-robot.9c39f6f21ffc.jpg&amp;amp;w=584&amp;amp;sig=31b0af03fddf44e776ad82dc66390d88cf1a1787 584w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/anki-vector-robot.9c39f6f21ffc.jpg&amp;amp;w=1168&amp;amp;sig=7a591d0d2abe2589ad9a21c433127ec5b0aae513 1168w, https://files.realpython.com/media/anki-vector-robot.9c39f6f21ffc.jpg 2336w&quot; sizes=&quot;75vw&quot; alt=&quot;ANKI&amp;#39;s Robot Vector&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/asins/B07G3ZNK4Y&quot;&gt;Get Vector on Amazon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/asins/B074WC4NHW/&quot;&gt;Get Cozmo on Amazon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.anki.com/blog/learn/tutorial/getting-started-with-the-cozmo-sdk/&quot;&gt;Python SDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;dji-drones&quot;&gt;DJI Drones&lt;/h3&gt;
&lt;p&gt;A little tired of being stuck on the ground? How about flying? DJI has multiple versions of their drones that you can take flight with either manually or automated using their software. If they don&amp;rsquo;t have the feature you&amp;rsquo;re looking for, how about checking out the SDK and doing it yourself! DJI has a lot of drone options available, but I&amp;rsquo;ve got my eye on the Tello, since it&amp;rsquo;s on the cheaper end and has an easier-to-use Python Library.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/ryze-tello-drone-full-720x479.1c550d13d65b.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-33&quot; src=&quot;https://files.realpython.com/media/ryze-tello-drone-full-720x479.1c550d13d65b.jpg&quot; width=&quot;720&quot; height=&quot;479&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ryze-tello-drone-full-720x479.1c550d13d65b.jpg&amp;amp;w=180&amp;amp;sig=a52b43de81169342987d777a5b875c117fcf2fed 180w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ryze-tello-drone-full-720x479.1c550d13d65b.jpg&amp;amp;w=360&amp;amp;sig=bd2de2af0334bbf8ada17205ec8f59828cee4a80 360w, https://files.realpython.com/media/ryze-tello-drone-full-720x479.1c550d13d65b.jpg 720w&quot; sizes=&quot;75vw&quot; alt=&quot;DJI&amp;#39;s Drone Tello&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/asins/B078YLX1XJ&quot;&gt;Get Tello on Amazon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/microlinux/tello&quot;&gt;Tello Python Library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;happy-pythonic-holidays&quot;&gt;Happy Pythonic Holidays&lt;/h2&gt;
&lt;p&gt;Whatever you decide to get you or your Python developer friends, be sure to enjoy this holiday season and keep on Pythoning! I&amp;rsquo;m about to push checkout on my Amazon cart now and tell my wife that #PythonMadeMeDoIt.&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>Thonny: The Beginner-Friendly Python Editor</title>
      <id>https://realpython.com/python-thonny/</id>
      <link href="https://realpython.com/python-thonny/"/>
      <updated>2018-12-12T14:00:00+00:00</updated>
      <summary>In this tutorial, you’ll learn all about Thonny, a free Python Integrated Development Environment (IDE) that was especially designed with the beginner Pythonista in mind. It has a built-in debugger and allows you to do step-through expression evaluation.</summary>
      <content type="html">
        &lt;p&gt;Are you a Python beginner looking for a tool that can support your learning? This article is for you! Every programmer needs a place to write their code. This article will discuss an awesome tool called Thonny that will enable you to start working with Python in a beginner-friendly environment. &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;How to install Thonny on your computer&lt;/li&gt;
&lt;li&gt;How to navigate Thonny&amp;rsquo;s user interface to use its built-in features&lt;/li&gt;
&lt;li&gt;How to use Thonny to write and run your code&lt;/li&gt;
&lt;li&gt;How to use Thonny to debug your code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the end of this article, you&amp;rsquo;ll be comfortable with the development workflow in Thonny and ready to use it for your Python learning.&lt;/p&gt;
&lt;p&gt;So what is Thonny? Great question!&lt;/p&gt;
&lt;p&gt;Thonny is a free Python Integrated Development Environment (IDE) that was especially designed with the beginner Pythonista in mind. Specifically, it has a built-in debugger that can help when you run into nasty bugs, and it offers the ability to do step through expression evaluation, among other really awesome features.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Sample Chapter:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-real-python-sample-chapter-experiment&quot; data-focus=&quot;false&quot;&gt;Download a free sample chapter from the Real Python course&lt;/a&gt; and gain practical Python programming skills.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;installing-thonny&quot;&gt;Installing Thonny&lt;/h2&gt;
&lt;p&gt;This article assumes that you have Python 3 installed on your computer. If not, please review &lt;a href=&quot;https://realpython.com/installing-python/&quot;&gt;Python 3 Installation &amp;amp; Setup&lt;/a&gt;. &lt;/p&gt;
&lt;h3 id=&quot;web-download&quot;&gt;Web Download&lt;/h3&gt;
&lt;p&gt;The web download can be accessed via a web browser by visiting the &lt;a href=&quot;https://thonny.org/&quot;&gt;Thonny website&lt;/a&gt;. Once on the page, you will see a light gray box in the top right corner like this: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-20_10.52.50.5b35603b597b.png&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/Screenshot_2018-10-20_10.52.50.5b35603b597b.png&quot; width=&quot;440&quot; height=&quot;170&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_10.52.50.5b35603b597b.png&amp;amp;w=110&amp;amp;sig=f56538b76aa2fcea73e21e697d8cd7bb7901c4ac 110w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_10.52.50.5b35603b597b.png&amp;amp;w=220&amp;amp;sig=d027c5cb18005c2951b73a25e674ca194f1b1f05 220w, https://files.realpython.com/media/Screenshot_2018-10-20_10.52.50.5b35603b597b.png 440w&quot; sizes=&quot;75vw&quot; alt=&quot;Thonny&amp;#39;s Web Download Widget&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve found the gray box, click the appropriate link for your operating system. This tutorial assumes you&amp;rsquo;ve downloaded version 3.0.1.&lt;/p&gt;
&lt;h3 id=&quot;command-line-download&quot;&gt;Command Line Download&lt;/h3&gt;
&lt;p&gt;You can also install Thonny via your system&amp;rsquo;s command line. On Windows, you can do this by starting a program called &lt;strong&gt;Command Prompt&lt;/strong&gt;, while on macOS and Linux you start a program called &lt;strong&gt;Terminal&lt;/strong&gt;. Once you&amp;rsquo;ve done that, enter 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 thonny
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-user-interface&quot;&gt;The User Interface&lt;/h2&gt;
&lt;p&gt;Let’s make sure you understand what Thonny has to offer. Think of Thonny as the workroom in which you will create amazing Python projects. Your workroom contains a toolbox containing many tools that will enable you to be a rock star Pythonista. In this section, you&amp;rsquo;ll learn about each of the features of the UI that&amp;rsquo;ll help you use each of the tools in your Thonny toolbox.&lt;/p&gt;
&lt;h3 id=&quot;the-code-editor-and-shell&quot;&gt;The Code Editor and Shell&lt;/h3&gt;
&lt;p&gt;Now that you have Thonny installed, open the application. You should see a window with several icons across the top, and two white areas:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-20_11.03.57.d46d970db1e6.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/Screenshot_2018-10-20_11.03.57.d46d970db1e6.png&quot; width=&quot;1402&quot; height=&quot;1266&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_11.03.57.d46d970db1e6.png&amp;amp;w=350&amp;amp;sig=251d62ec20bd108ff02d8104a496af6da9300447 350w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_11.03.57.d46d970db1e6.png&amp;amp;w=701&amp;amp;sig=4065a6ab6220c234d23144d1d3502adc774e08af 701w, https://files.realpython.com/media/Screenshot_2018-10-20_11.03.57.d46d970db1e6.png 1402w&quot; sizes=&quot;75vw&quot; alt=&quot;Thonny&amp;#39;s Main UI&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Notice the two main sections of the window. The top section is your code editor, where you will write all of your code. The bottom half is your Shell, where you will see outputs from your code.&lt;/p&gt;
&lt;h3 id=&quot;the-icons&quot;&gt;The Icons&lt;/h3&gt;
&lt;p&gt;Across the top you&amp;rsquo;ll see several icons. Let’s explore what each of them does. You&amp;rsquo;ll see an image of the icons below, with a letter above each one. We will use these letters to talk about each of the icons:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-20_11.09.16.7c059cfba13c.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-66&quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-10-20_11.09.16.7c059cfba13c.png&quot; width=&quot;902&quot; height=&quot;180&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_11.09.16.7c059cfba13c.png&amp;amp;w=225&amp;amp;sig=19999c74676ba82a6d41a86ed3f6c2332a808985 225w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_11.09.16.7c059cfba13c.png&amp;amp;w=451&amp;amp;sig=d40424a5c8c1a6de1612528c755e950e68f73090 451w, https://files.realpython.com/media/Screenshot_2018-10-20_11.09.16.7c059cfba13c.png 902w&quot; sizes=&quot;75vw&quot; alt=&quot;The Icons Across the Top of Thonny&amp;#39;s UI&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Working our way from left to right, below is a description of each of the icons in the image.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; The paper icon allows you to create a new file. Typically in Python you want to separate your programs into separate files. You&amp;rsquo;ll use this button later in the tutorial to create your first program in Thonny!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;B:&lt;/strong&gt; The open folder icon allows you to open a file that already exists on your computer. This might be useful if you come back to a program that you worked on previously.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;C:&lt;/strong&gt; The floppy disk icon allows you to save your code. Press this early and often. You&amp;rsquo;ll use this later to save your first Thonny Python program.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;D:&lt;/strong&gt; The play icon allows you to run your code. Remember that the code you write is meant to be executed. Running your code means you&amp;rsquo;re telling Python, “Do what I told you to do!” (In other words, &amp;ldquo;Read through my code and execute what I wrote.&amp;rdquo;)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;E:&lt;/strong&gt; The bug icon allows you to debug your code. It’s inevitable that you will encounter bugs when you&amp;rsquo;re writing code. A bug is another word for a problem. Bugs can come in many forms, sometimes appearing when you use inappropriate syntax and sometimes when your logic is incorrect. &lt;/p&gt;
&lt;p&gt;Thonny’s bug button is typically used to spot and investigate bugs. You&amp;rsquo;ll work with this later in the tutorial. By the way, if you&amp;rsquo;re wondering why they&amp;rsquo;re called bugs, there&amp;rsquo;s also a fun &lt;a href=&quot;http://www.computerhistory.org/tdih/september/9/&quot;&gt;story of how it came about!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;F-H:&lt;/strong&gt; The arrow icons allow you to run your programs step by step. This can be very useful when you&amp;rsquo;re debugging or, in other words, trying to find those nasty bugs in your code. These icons are used after you press the bug icon. You’ll notice as you hit each arrow, a yellow highlighted bar will indicate which line or section Python is currently evaluating:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;F&lt;/strong&gt; arrow tells Python to take a big step, meaning jumping to the next line or block of code. &lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;G&lt;/strong&gt; arrow tells Python to take a small step, meaning diving deep into each component of an expression. &lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;H&lt;/strong&gt; arrow tells Python to exit out of the debugger.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;I:&lt;/strong&gt; The resume icon allows you to return to play mode from debug mode. This is useful in the instance when you no longer want to go step by step through the code, and instead want your program to finish running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;J:&lt;/strong&gt; The stop icon allows you to stop running your code. This can be particularly useful if, let&amp;rsquo;s say, your code runs a program that opens a new window, and you want to stop that program. You&amp;rsquo;ll use the stop icon later in the tutorial.&lt;/p&gt;
&lt;h4 id=&quot;lets-try-it&quot;&gt;Let’s Try It!&lt;/h4&gt;
&lt;p&gt;Get ready to write your first official Python program in Thonny:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Enter the following code into the code 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;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;Hello World&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the play button to run your program.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;See the output in the Shell window.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the play button again to see that it says hello one more time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Congratulations! You&amp;rsquo;ve now completed your first program in Thonny! You should see &lt;code&gt;Hello world!&lt;/code&gt; printed inside the Shell, also known as the console. This is because your program told Python to print this phrase, and the console is where you see the output of this execution. &lt;/p&gt;
&lt;h3 id=&quot;other-ui-features&quot;&gt;Other UI Features&lt;/h3&gt;
&lt;p&gt;To see more of the other features that Thonny has to offer, navigate to the menu bar and select the &lt;em&gt;View&lt;/em&gt; dropdown. You should see that &lt;em&gt;Shell&lt;/em&gt; has a check mark next to it, which is why you see the Shell section in Thonny’s application window:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-20_11.15.24.2f90db2108e7.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-33&quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-10-20_11.15.24.2f90db2108e7.png&quot; width=&quot;438&quot; height=&quot;754&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_11.15.24.2f90db2108e7.png&amp;amp;w=109&amp;amp;sig=7d8665469a9fcc31b08237427ac31cfe27663b5c 109w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_11.15.24.2f90db2108e7.png&amp;amp;w=219&amp;amp;sig=88113776db9941d54a9fbc6b7fd92c854054fbf1 219w, https://files.realpython.com/media/Screenshot_2018-10-20_11.15.24.2f90db2108e7.png 438w&quot; sizes=&quot;75vw&quot; alt=&quot;Thonny&amp;#39;s &amp;quot;View&amp;quot; Dropdown&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let’s explore some of the other offerings, specifically those that will be useful to a beginning Pythonista:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Help:&lt;/strong&gt; You&amp;rsquo;ll select the &lt;em&gt;Help&lt;/em&gt; view if you want more information about working with Thonny. Currently this section offers more reading on the following topics: &lt;em&gt;Running Programs Step-wise,&lt;/em&gt; how to install &lt;em&gt;3rd Party Packages&lt;/em&gt;, or &lt;em&gt;using Scientific Python Packages&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Variables:&lt;/strong&gt; This feature can be very valuable. A variable in Python is a value that you define in code. Variables can be numbers, strings, or other complex data structures. This section allows you to see the values assigned to all of the &lt;a href=&quot;https://realpython.com/python-variables/&quot;&gt;variables&lt;/a&gt; in your program. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Assistant:&lt;/strong&gt; The Assistant is there to give you helpful hints when you hit Exceptions or other types of errors.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The other features will become useful as you advance your skills. Check them out once you get more comfortable with Thonny!&lt;/p&gt;
&lt;h2 id=&quot;the-code-editor&quot;&gt;The Code Editor&lt;/h2&gt;
&lt;p&gt;Now that you have an understanding of the UI, let’s use Thonny to write another little program. In this section, you&amp;rsquo;ll go through the features of Thonny that will help guide you through your development workflow.&lt;/p&gt;
&lt;h3 id=&quot;write-some-code&quot;&gt;Write Some Code&lt;/h3&gt;
&lt;p&gt;In the code editor (top portion of the UI), add the following 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;factorial&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;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;1&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;mi&quot;&gt;1&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;return&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;n&quot;&gt;factorial&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;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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factorial&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;h3 id=&quot;save-your-code&quot;&gt;Save Your Code&lt;/h3&gt;
&lt;p&gt;Before we move on, let’s save your program. Last time, you were prompted to do this after pressing the play button. You can also do this by clicking the blue floppy disk icon or by going to the menu bar and selecting &lt;em&gt;File&lt;/em&gt; &amp;gt; &lt;em&gt;Save&lt;/em&gt;. Let’s call the program &lt;code&gt;factorial.py&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;run-your-code&quot;&gt;Run Your Code&lt;/h3&gt;
&lt;p&gt;In order to run your code, find and press the play icon. The output should look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-11_23.49.22.af82669bc586.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/Screenshot_2018-10-11_23.49.22.af82669bc586.png&quot; width=&quot;1458&quot; height=&quot;1194&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-11_23.49.22.af82669bc586.png&amp;amp;w=364&amp;amp;sig=b2271c74ea0028b24688307e0c29209f3848eefe 364w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-11_23.49.22.af82669bc586.png&amp;amp;w=729&amp;amp;sig=1ea44c32dfe74b9e4694d590cf79210669130eae 729w, https://files.realpython.com/media/Screenshot_2018-10-11_23.49.22.af82669bc586.png 1458w&quot; sizes=&quot;75vw&quot; alt=&quot;Output of factorial function&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;debug-your-code&quot;&gt;Debug Your Code&lt;/h3&gt;
&lt;p&gt;To truly understand what this function is doing, try the step feature. Take a few large and small steps through the function to see what is happening. Remember you can do this by pressing the arrow icons:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-23_22.47.50.5613862c2c62.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/Screenshot_2018-10-23_22.47.50.5613862c2c62.png&quot; width=&quot;1562&quot; height=&quot;1180&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-23_22.47.50.5613862c2c62.png&amp;amp;w=390&amp;amp;sig=c620871e3a25913d0828935b34b063f69f9d6c3a 390w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-23_22.47.50.5613862c2c62.png&amp;amp;w=781&amp;amp;sig=037b90285d22aaccb339d93cf21dc47e5b17b3ab 781w, https://files.realpython.com/media/Screenshot_2018-10-23_22.47.50.5613862c2c62.png 1562w&quot; sizes=&quot;75vw&quot; alt=&quot;Step by step windows&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the steps will show how the computer is evaluating each part of the code. Each pop up window is like a piece of scratch paper that the computer is using to compute each portion of the code. Without this awesome feature, this may have been hard to conceptualize&amp;mdash;but now you&amp;rsquo;ve got it!&lt;/p&gt;
&lt;h3 id=&quot;stop-running-your-code&quot;&gt;Stop Running Your Code&lt;/h3&gt;
&lt;p&gt;So far, there hasn&amp;rsquo;t been a need to hit the stop icon for this program, particularly because it exits as soon as it has executed  &lt;code&gt;print()&lt;/code&gt;. Try increasing the number being passed to the factorial function to &lt;code&gt;100&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;factorial&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;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;1&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;mi&quot;&gt;1&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;return&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;n&quot;&gt;factorial&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;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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factorial&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then step through the function. After a while, you will notice that you will be clicking for a long time to reach the end. This is a good time to use the stop button. The stop button can be really useful to stop a program that is either intentionally or unintentionally running continuously.&lt;/p&gt;
&lt;h3 id=&quot;find-syntax-errors-in-your-code&quot;&gt;Find Syntax Errors in Your Code&lt;/h3&gt;
&lt;p&gt;Now that you have a simple program that works, let’s break it! By intentionally creating an error in your factorial program, you&amp;rsquo;ll be able to see how Thonny handles these types of issues.&lt;/p&gt;
&lt;p&gt;We will be creating what is called a &lt;strong&gt;syntax error&lt;/strong&gt;. A syntax error is an error that indicates that your code is syntactically incorrect. In other words, your code does not follow the proper way to write Python. When Python notices the error, it will display a syntax error to complain about your invalid code.&lt;/p&gt;
&lt;p&gt;Above the print statement, let&amp;rsquo;s add another print statement that says &lt;code&gt;print(&quot;The factorial of 100 is:&quot;)&lt;/code&gt;. Now let&amp;rsquo;s go ahead and create syntax errors. In the first print statement, remove the second quotation mark, and in the other remove the second parenthesis. &lt;/p&gt;
&lt;p&gt;As you do this, you should see that Thonny will highlight your &lt;code&gt;SyntaxErrors&lt;/code&gt;. Missing quotations are highlighted in green, and missing parenthesis are in gray:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-12_00.11.56.451e383e9c31.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-75&quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-10-12_00.11.56.451e383e9c31.png&quot; width=&quot;1038&quot; height=&quot;302&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-12_00.11.56.451e383e9c31.png&amp;amp;w=259&amp;amp;sig=f2e4c301111428f7d32b3c1c69f157381c468327 259w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-12_00.11.56.451e383e9c31.png&amp;amp;w=519&amp;amp;sig=a25341206825936897c5f86da2ebe6c039de8b00 519w, https://files.realpython.com/media/Screenshot_2018-10-12_00.11.56.451e383e9c31.png 1038w&quot; sizes=&quot;75vw&quot; alt=&quot;syntax errors for factorial function&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For beginners, this is a great resource that will allow you to help spot any typos while you&amp;rsquo;re writing. Some of the most common and frustrating errors when you start programming are missing quotes and mismatched parentheses.&lt;/p&gt;
&lt;p&gt;If you have your &lt;em&gt;Assistant View&lt;/em&gt; turned on, you will also notice that it will give you a helpful message to guide you in the right direction when you are debugging:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-20_10.18.50.1f3845020f38.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-75&quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-10-20_10.18.50.1f3845020f38.png&quot; width=&quot;796&quot; height=&quot;790&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_10.18.50.1f3845020f38.png&amp;amp;w=199&amp;amp;sig=56c5a08dac90f24f44282f4eb3b017c6881f5e16 199w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_10.18.50.1f3845020f38.png&amp;amp;w=398&amp;amp;sig=88a60db9f807499789aa900765b1f39fcae812df 398w, https://files.realpython.com/media/Screenshot_2018-10-20_10.18.50.1f3845020f38.png 796w&quot; sizes=&quot;75vw&quot; alt=&quot;Shows assistant showing syntax error help text&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you get more comfortable with Thonny, the Assistant can be a useful tool to help you get unstuck!&lt;/p&gt;
&lt;h2 id=&quot;the-package-manager&quot;&gt;The Package Manager&lt;/h2&gt;
&lt;p&gt;As you continue to learn Python, it can be quite useful to download a Python package to use inside of your code. This allows you to use code that someone else has written inside of your program. &lt;/p&gt;
&lt;p&gt;Consider an example where you want to do some calculations in your code. Instead of writing your own calculator, you might want to use a &lt;a href=&quot;https://pypi.org/project/simplecalculator/&quot;&gt;third-party package&lt;/a&gt; called &lt;code&gt;simplecalculator&lt;/code&gt;. In order to do this, you&amp;rsquo;ll use Thonny&amp;rsquo;s package manager.  &lt;/p&gt;
&lt;p&gt;The package manager will allow you to install packages that you will need to use with your program. Specifically, it allows you to add more tools to your toolbox. Thonny has the built-in benefit of handling any conflicts with other Python interpreters. &lt;/p&gt;
&lt;p&gt;To access the package manager, go to the menu bar and select &lt;em&gt;Tools&lt;/em&gt; &amp;gt; &lt;em&gt;Manage Packages&amp;hellip;&lt;/em&gt; This should pop open a new window with a search field. Type &lt;code&gt;simplecalculator&lt;/code&gt; into that field and click the &lt;em&gt;Search&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;The output should look similar to this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-11_23.22.41.544b108e9748.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/Screenshot_2018-10-11_23.22.41.544b108e9748.png&quot; width=&quot;1652&quot; height=&quot;656&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-11_23.22.41.544b108e9748.png&amp;amp;w=413&amp;amp;sig=95c3c2171505b0db6b4ea56b3fe14fa888e83b54 413w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-11_23.22.41.544b108e9748.png&amp;amp;w=826&amp;amp;sig=b529de2733251578bf4f2dd9dc24cd29f2f18176 826w, https://files.realpython.com/media/Screenshot_2018-10-11_23.22.41.544b108e9748.png 1652w&quot; sizes=&quot;75vw&quot; alt=&quot;Installed simplecalculator package&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Go ahead and click &lt;em&gt;Install&lt;/em&gt; to install this package. You will see a small window pop up showing the system&amp;rsquo;s logs while it installs the package. Once it completes, you are ready to use &lt;code&gt;simplecalculator&lt;/code&gt; in your code!&lt;/p&gt;
&lt;p&gt;In the next section, you will use the &lt;code&gt;simplecalculator&lt;/code&gt; package along with some of the other skills you&amp;rsquo;ve learned in this tutorial to create a simple calculator program.&lt;/p&gt;
&lt;h2 id=&quot;check-your-understanding&quot;&gt;Check Your Understanding&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve learned so much about Thonny so far! Here&amp;rsquo;s what you&amp;rsquo;ve learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where to write your code&lt;/li&gt;
&lt;li&gt;How to save your code&lt;/li&gt;
&lt;li&gt;How to run your code&lt;/li&gt;
&lt;li&gt;How to stop your code from running&lt;/li&gt;
&lt;li&gt;Where to see your code execute&lt;/li&gt;
&lt;li&gt;How to spot &lt;code&gt;SyntaxErrors&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;How to install third party packages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s check your understanding of these concepts.&lt;/p&gt;
&lt;p&gt;Now that you have &lt;code&gt;simplecalculator&lt;/code&gt; installed, let’s create a simple program that will use this package. You&amp;rsquo;ll also use this as an opportunity to check that you understand some of the UI and development features that you&amp;rsquo;ve learned thus far in the tutorial. &lt;/p&gt;
&lt;h3 id=&quot;part-1-create-a-file-add-some-code-and-understand-the-code&quot;&gt;Part 1: Create a File, Add Some Code, and Understand the Code&lt;/h3&gt;
&lt;p&gt;In Part 1, you will create a file, and add some code to it! Do your best to try to dig into what the code is actually doing. If you get stuck, check out the &lt;em&gt;Take a Deeper Look&lt;/em&gt; window. Let&amp;rsquo;s get started:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start a new file.&lt;/li&gt;
&lt;li&gt;Add the following code into your Thonny code editor:&lt;/li&gt;
&lt;/ol&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;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;calculator.simple&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SimpleCalculator&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_calculator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SimpleCalculator&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;my_calculator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;2 * 2&amp;#39;&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;my_calculator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lcd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code will print out the result of &lt;code&gt;2 * 2&lt;/code&gt; to the Thonny Shell in the main UI. To understand what each part of the code is doing, check out the &lt;em&gt;Take a Deeper Look&lt;/em&gt; section below.&lt;/p&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_card058ed5&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;#collapse058ed5&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse058ed5&quot;&gt;Take a Deeper Look&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapse058ed5&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse058ed5&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapse058ed5&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_card058ed5&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 1:&lt;/strong&gt; This code imports the library &lt;code&gt;calculator&lt;/code&gt; inside of the package called &lt;code&gt;simplecalculator&lt;/code&gt;. From this library, we import the class called &lt;code&gt;SimpleCalculator&lt;/code&gt; from a file called &lt;code&gt;simple.py&lt;/code&gt;. You can see the code &lt;a href=&quot;https://pypi.org/project/simplecalculator/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lines 2:&lt;/strong&gt; This is a blank line behind code blocks, which is generally a preferred style. Read more about &lt;a href=&quot;https://realpython.com/python-code-quality/&quot;&gt;Python Code Quality in this article&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 3:&lt;/strong&gt; Here we create an instance of the class &lt;code&gt;SimpleCalculator&lt;/code&gt; and assign it to a variable called &lt;code&gt;my_calculator&lt;/code&gt;. This can be used to run different calculators. If you’re new to classes, you can learn more about object-oriented programming &lt;a href=&quot;https://realpython.com/python3-object-oriented-programming/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 4:&lt;/strong&gt; Here we have the calculator run the operation &lt;code&gt;2 * 2&lt;/code&gt; by calling &lt;code&gt;run()&lt;/code&gt; and passing in the expression as a string.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 5:&lt;/strong&gt; Here we print the result of the calculation. You’ll notice in order to get the most recent calculation result, we must access the attribute called &lt;code&gt;lcd&lt;/code&gt;. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;Great! Now that you know exactly what your calculator code is doing, let&amp;rsquo;s move on to running this code!&lt;/p&gt;
&lt;h3 id=&quot;part-2-save-the-file-view-the-variables-and-run-your-code&quot;&gt;Part 2: Save the File, View the Variables, and Run Your Code&lt;/h3&gt;
&lt;p&gt;Now it&amp;rsquo;s time to save and run your code. In this section, you&amp;rsquo;ll make use of two of the icons we reviewed earlier:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Save your new file as &lt;code&gt;calculations.py&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Open the &lt;em&gt;Variables&lt;/em&gt; window and make note of the two variables listed. You should see &lt;code&gt;SimpleCalculator&lt;/code&gt; and &lt;code&gt;my_calculator&lt;/code&gt;. This section also gives you insight into the value that each variable is pointing to. &lt;/li&gt;
&lt;li&gt;Run your code! You should see &lt;code&gt;4.0&lt;/code&gt; in the output:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-20_11.18.36.528db7d62861.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/Screenshot_2018-10-20_11.18.36.528db7d62861.png&quot; width=&quot;1900&quot; height=&quot;1036&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_11.18.36.528db7d62861.png&amp;amp;w=475&amp;amp;sig=e2d3bbb31dfce867bc0b987df48c6a35effdbaff 475w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-20_11.18.36.528db7d62861.png&amp;amp;w=950&amp;amp;sig=b187945962913acc1e8ba1d922c4f0db2e555d7e 950w, https://files.realpython.com/media/Screenshot_2018-10-20_11.18.36.528db7d62861.png 1900w&quot; sizes=&quot;75vw&quot; alt=&quot;Calculator with Simple Expression&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Great job! Next you&amp;rsquo;ll explore how Thonny’s debugger can help you to better understand this code.&lt;/p&gt;
&lt;h2 id=&quot;other-great-beginner-features&quot;&gt;Other Great Beginner Features&lt;/h2&gt;
&lt;p&gt;As you get more comfortable with Thonny, the features in this section will come in quite handy. &lt;/p&gt;
&lt;h3 id=&quot;debugging&quot;&gt;Debugging&lt;/h3&gt;
&lt;p&gt;Using your &lt;code&gt;calculations.py&lt;/code&gt; script, you&amp;rsquo;re going to use the debugger to investigate what is happening. Update your code in &lt;code&gt;calculations.py&lt;/code&gt; 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;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;calculator.simple&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SimpleCalculator&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_add_string&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;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;#39;&amp;#39;&amp;#39;Returns a string containing an addition expression.&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;x + y&amp;#39;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;my_calculator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SimpleCalculator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  
&lt;span class=&quot;n&quot;&gt;my_calculator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_add_string&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;2&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;my_calculator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lcd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hit the save icon to save this version.&lt;/p&gt;
&lt;p&gt;You’ll notice the code has a new function called &lt;code&gt;create_add_string()&lt;/code&gt;. If you’re unfamiliar with Python functions, learn more in &lt;a href=&quot;https://realpython.com/products/real-python-course/&quot;&gt;this awesome Real Python course&lt;/a&gt;! &lt;/p&gt;
&lt;p&gt;As you inspect the function, you may notice why this script will not work as expected. If not, that’s okay! Thonny is going to help you see exactly what is going on, and squash that bug! Go ahead and run your program and see what happens. The Shell output should be 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;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calculations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;py&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Oh no! Now you can see there is a bug in your program. The answer should be 4! Next, you&amp;rsquo;ll use Thonny’s debugger to find the bug. &lt;/p&gt;
&lt;h4 id=&quot;lets-try-it_1&quot;&gt;Let’s Try It!&lt;/h4&gt;
&lt;p&gt;Now that we have a bug in our program, this is a great chance to use Thonny&amp;rsquo;s debugging features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click the bug icon at the top of the window. This enters debugger mode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You should see the import statements highlighted. Click the small step arrow icon, the yellow arrow in the middle. Keep pressing this to see how the debugger works. You should notice that it highlights each step that Python takes to evaluate your program. Once it hits &lt;code&gt;create_add_string()&lt;/code&gt;, you should see a new window pop up.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Examine the pop up window carefully. You should see that it shows the values for x and y. Keep pressing the small step icon until you see the value that Python will return to your program. It will be enclosed in a light-blue box: &lt;a href=&quot;https://files.realpython.com/media/create_add_string.f45ac64b9aaf.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-75&quot; src=&quot;https://files.realpython.com/media/create_add_string.f45ac64b9aaf.png&quot; width=&quot;818&quot; height=&quot;564&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/create_add_string.f45ac64b9aaf.png&amp;amp;w=204&amp;amp;sig=8134eb23b46bd0ba0f0247ccca3c062a8624924e 204w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/create_add_string.f45ac64b9aaf.png&amp;amp;w=409&amp;amp;sig=47485ac9958fac77b9cf1cce823987de9bef7963 409w, https://files.realpython.com/media/create_add_string.f45ac64b9aaf.png 818w&quot; sizes=&quot;75vw&quot; alt=&quot;Thonny&amp;#39;s Function Debug Pop-up Window&quot;/&gt;&lt;/a&gt;
 Oh no! There’s the bug! It looks like Python will return a string containing the letters &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; (meaning &lt;code&gt;&#39;x + y&#39;&lt;/code&gt; and not a string containing the values of those variables, like &lt;code&gt;&#39;2 + 2&#39;&lt;/code&gt;, which is what the calculator is expecting.) Each time you see a light-blue box, you can think of this as Python replacing subexpressions with their values, step by step. The pop up window can be thought of as a piece of scratch paper that Python uses to figure out those values. Continue to step through the program to see how this bug results in a calculation of &lt;code&gt;0&lt;/code&gt;. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The bug here has to do with string formatting. If you are unfamiliar with string formatting, check out this article on &lt;a href=&quot;https://realpython.com/python-string-formatting/&quot;&gt;Python String Formatting Best Practices&lt;/a&gt;. Inside &lt;code&gt;create_add_string()&lt;/code&gt;, &lt;a href=&quot;https://realpython.com/python-f-strings/&quot;&gt;the f-string formatting method&lt;/a&gt; should be used. Update this function 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_add_string&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;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;#39;&amp;#39;&amp;#39;Returns a string containing an addition expression.&amp;#39;&amp;#39;&amp;#39;&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;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{x}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; + &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{y}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run your program again. You should see the following output:&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;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calculations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;py&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;4.0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Success! You have just demonstrated how the step-by-step debugger can help you find a problem in your code! Next you&amp;rsquo;ll learn about some other fun Thonny features.&lt;/p&gt;
&lt;h3 id=&quot;variable-scope-highlighting&quot;&gt;Variable Scope Highlighting&lt;/h3&gt;
&lt;p&gt;Thonny offers variable highlighting to remind you that the same name doesn&amp;rsquo;t always mean the same variable. In order for this feature to work, on the menu bar, go to &lt;em&gt;Thonny&lt;/em&gt; &amp;gt; &lt;em&gt;Preferences&lt;/em&gt; and ensure that &lt;em&gt;Highlight matching names&lt;/em&gt; is checked.&lt;/p&gt;
&lt;p&gt;Notice in the code snippet below, that &lt;code&gt;create_add_string()&lt;/code&gt; now has a new variable called &lt;code&gt;my_calculator&lt;/code&gt;, though this is not the same as the &lt;code&gt;my_calculator&lt;/code&gt; on lines 10 and 11. You should be able to tell because Thonny highlights the variables that reference the same thing. This &lt;code&gt;my_calculator&lt;/code&gt; inside the function only exists within the scope of that function, which is why it is not highlighted when the cursor is on the other &lt;code&gt;my_calculator&lt;/code&gt; variable on line 10:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-10-11_23.37.10.0579f93ea6df.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-75&quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-10-11_23.37.10.0579f93ea6df.png&quot; width=&quot;1110&quot; height=&quot;422&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-11_23.37.10.0579f93ea6df.png&amp;amp;w=277&amp;amp;sig=d75239b3514603e54d94a2b03fe523d391f28d89 277w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-10-11_23.37.10.0579f93ea6df.png&amp;amp;w=555&amp;amp;sig=6b9c45da5c494ebc111e8b0acd33f723469bdc30 555w, https://files.realpython.com/media/Screenshot_2018-10-11_23.37.10.0579f93ea6df.png 1110w&quot; sizes=&quot;75vw&quot; alt=&quot;Calculator with highlighting&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This feature can really help you avoid typos and understand the scope of your variables.&lt;/p&gt;
&lt;h3 id=&quot;code-completion&quot;&gt;Code Completion&lt;/h3&gt;
&lt;p&gt;Thonny also offers code completion for APIs. Notice in the snapshot below how pressing the &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-tab&quot;&gt;Tab&lt;/kbd&gt;&lt;/span&gt; key shows the methods available from the &lt;code&gt;random&lt;/code&gt; library:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/code_complete.d1514f5cc85f.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-66&quot; src=&quot;https://files.realpython.com/media/code_complete.d1514f5cc85f.png&quot; width=&quot;732&quot; height=&quot;336&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/code_complete.d1514f5cc85f.png&amp;amp;w=183&amp;amp;sig=417ba6ea3606bd37616019eabc0da307a0961998 183w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/code_complete.d1514f5cc85f.png&amp;amp;w=366&amp;amp;sig=012ae852f01a60869f40717d04884fd18269ae33 366w, https://files.realpython.com/media/code_complete.d1514f5cc85f.png 732w&quot; sizes=&quot;75vw&quot; alt=&quot;Thonny&amp;#39;s Code Complete Feature&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This can be very useful when you&amp;rsquo;re working with libraries and don&amp;rsquo;t want to look at the documentation to find a method or attribute name.&lt;/p&gt;
&lt;h3 id=&quot;working-on-a-pre-existing-project&quot;&gt;Working on a Pre-Existing Project&lt;/h3&gt;
&lt;p&gt;Now that you&amp;rsquo;ve learned the basic features of Thonny, let’s explore how you can use it to work on a pre-existing project.&lt;/p&gt;
&lt;h4 id=&quot;find-a-file-on-your-computer&quot;&gt;Find a File on Your Computer&lt;/h4&gt;
&lt;p&gt;Opening a file on your computer is as easy as going to the menu bar, selecting &lt;em&gt;File&lt;/em&gt; &amp;gt; &lt;em&gt;Open&lt;/em&gt;, and using your browser to navigate to the file. You can also use the open folder icon at the top of the screen to do this as well.&lt;/p&gt;
&lt;p&gt;If you have a &lt;code&gt;requirements.txt&lt;/code&gt; file and &lt;code&gt;pip&lt;/code&gt; locally installed, you can &lt;code&gt;pip install&lt;/code&gt; these from the Thonny system Shell. If you don&amp;rsquo;t have pip installed, remember you can use the &lt;a href=&quot;#the-package-manager&quot;&gt;Package Manager&lt;/a&gt; to install it:&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 -r requirements.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&quot;work-on-a-project-from-github&quot;&gt;Work on a Project From Github&lt;/h4&gt;
&lt;p&gt;Now that you are a Thonny expert, you can use it to work on the exercises from &lt;em&gt;Real Python Course 1: Introduction to Python&lt;/em&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the &lt;em&gt;Real Python&lt;/em&gt; GitHub repo called &lt;a href=&quot;https://github.com/realpython/book1-exercises&quot;&gt;book1-exercises&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the green button labeled &lt;em&gt;Clone or download&lt;/em&gt; and select &lt;em&gt;Download Zip&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the opening folder icon to navigate and find the downloaded files. You should find a folder called &lt;code&gt;book1-exercises1&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open one of the files and start working!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is useful because there are tons of cool projects available on GitHub!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Awesome job getting through this tutorial on Thonny!&lt;/p&gt;
&lt;p&gt;You can now start using Thonny to write, debug, and run Python code! If you like Thonny, you might also like some of the other IDEs we&amp;rsquo;ve listed in &lt;a href=&quot;https://realpython.com/python-ides-code-editors-guide/&quot;&gt;Python IDEs and Code Editors (Guide)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thonny is actively maintained, and new features are being added all the time. There are several awesome new features that are currently in beta that can be found on the &lt;a href=&quot;https://thonny.org/blog/&quot;&gt;Thonny Blog&lt;/a&gt;. Thonny&amp;rsquo;s main development takes place at the &lt;a href=&quot;https://www.cs.ut.ee/et&quot;&gt;Institute of Computer Science&lt;/a&gt; of the &lt;a href=&quot;https://www.ut.ee/et&quot;&gt;University of Tartu&lt;/a&gt;, Estonia, as well as by contributors around the world.&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 Brian Peterson</title>
      <id>https://realpython.com/interview-brian-peterson/</id>
      <link href="https://realpython.com/interview-brian-peterson/"/>
      <updated>2018-12-10T14:00:00+00:00</updated>
      <summary>Brian is a project manager by day, and by night he&#39;s one of the moderators of the Pythonista Café. In our interview, we talk about how Python helps him in his role as a project manager, and how moderating a large forum for Python enthusiasts has impacted his coding chops.</summary>
      <content type="html">
        &lt;p&gt;To date, I&amp;rsquo;ve interviewed people you&amp;rsquo;ve likely heard of before from the Python community. But this column isn&amp;rsquo;t just about interviewing the rock stars and core devs. It&amp;rsquo;s also a means to shine light on the huge contributions to the community that can often go unthanked and overlooked. As such, I present to you Brian Peterson. &lt;/p&gt;
&lt;p&gt;Brian is a project manager by day, and by night he&amp;rsquo;s one of the moderators of the &lt;a href=&quot;https://www.pythonistacafe.com/&quot;&gt;Pythonista Café&lt;/a&gt;, a peer-to-peer learning community for Pythonistas. In our interview, we talk about how Python helps him in his role as a project manager, and how moderating a large forum for Python enthusiasts has impacted his coding chops. Let’s dig in!&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Welcome to Real Python! Let’s get started with the question I ask everyone. How did you get into programming, and when did you start using Python?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brian:&lt;/strong&gt; Well… thinking back, my first real exposure to programming was testing near object detection system prototypes using an old HP-87. The program was used to move and change target and rotating antenna positions along test tracks inside an automotive laboratory anechoic chamber, all while collecting and processing data from a spectrum analyzer. Seeing the code translate into motion did it for me&amp;mdash;something real, and tangible. I was hooked on using programming as an R&amp;amp;D tool to make things come alive.&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3 rounded-circle&quot; src=&quot;https://files.realpython.com/media/bpeterson.8bf67185705b.jpg&quot; width=&quot;555&quot; height=&quot;555&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bpeterson.8bf67185705b.jpg&amp;amp;w=138&amp;amp;sig=dc0f1d227948334a139f5ae947da2ff53a7140bf 138w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bpeterson.8bf67185705b.jpg&amp;amp;w=277&amp;amp;sig=392a18ef2eb3fc8a2e37298fb009932fff7d5993 277w, https://files.realpython.com/media/bpeterson.8bf67185705b.jpg 555w&quot; sizes=&quot;75vw&quot; alt=&quot;Brian Peterson&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Over the years, I started spending more time with Linux automation, control systems, data collection and analysis, which naturally led to spending more time writing C code, and then it was like, &amp;ldquo;Hey, Python is already on the system. Why not take it for a spin?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I think it was Python 2.3 at that time. I liked Python because it felt natural, and I didn’t have to throw away all my C knowledge. It had some functional programming sprinkled in, and an impressive set of engineering and scientific libraries. To top it off, I could use the same language seamlessly across scripting, interactive data analysis, and writing code. And Python syntax, well, it can be so darned readable that personally, I think it’s hard not to like it. I was hooked.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Working in project management and using Python might not seem a natural fit for some people. How do you use Python to help you in the project management office, and what tools and libraries are the most helpful for you on a day-to-day basis?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brian:&lt;/strong&gt; Yeah, at first glance Python may not seem like a natural fit. This is in large part due to the way project management is commonly taught&amp;mdash;like cooking from a recipe book rather than learning and understanding the culinary arts.&lt;/p&gt;
&lt;p&gt;And cookie-cutter project management tools are like the microwave ovens. Yeah, sure, use &amp;lsquo;em for the prepacked, boxed, canned stuff, but for every meal? Seriously?
Python also starts making sense when you&amp;rsquo;re considering different flavors of project management:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Traditional project managers use template-driven approaches.&lt;/li&gt;
&lt;li&gt;Agile practitioners use story-driven approaches.&lt;/li&gt;
&lt;li&gt;Adaptive practitioners blend existing, new, emerging approaches and creativity to develop the best fit for each unique project or group of projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So while off-the-shelf project management tools and services are an excellent fit for traditional projects, and a good fit for some Agile projects, adaptive project management calls for a different breed. In other words, rather than force all projects to fit a tool, why not use Python to construct a toolbox filled with best-of-breed tools to solve different types of problems for different kinds of projects?&lt;/p&gt;
&lt;p&gt;It’s also worth considering that modern day Project Management Offices (PMO) don’t just manage projects: there’s ideation, portfolio management, road mapping, strategic planning, KPI monitoring, communications, governance, dashboards, stakeholder portals, working closely with DevOps, business analysts, architects, developers, UX designers, CX folks, subject matter experts, other technologists, and the list goes on and on.&lt;/p&gt;
&lt;p&gt;Now imagine a vendor who knows nothing about your particular projects or PMO services with a big box of Legos gluing pieces into place and selling it to you. Yeah, it’s probably a nice contraption, or it wouldn’t be on the market for long.&lt;/p&gt;
&lt;p&gt;But compare it to owning your own big box of Legos (Python and goodies), where you can add Legos to pre-built contraptions, use Legos to bridge multiple contraptions together, use them with existing Legos already lying around the organization, or build your own and mix and match any or all of the above.&lt;/p&gt;
&lt;p&gt;And to answer the second part of the question, I currently use &lt;a href=&quot;https://realpython.com/tutorials/api/&quot;&gt;Python and RESTful APIs&lt;/a&gt; to interact with a variety of flexible SaaS tools, for example, &lt;a href=&quot;https://www.productplan.com/&quot;&gt;ProductPlan&lt;/a&gt;, &lt;a href=&quot;https://www.smartsheet.com/&quot;&gt;Smartsheet&lt;/a&gt; (with the dashboard extension), &lt;a href=&quot;https://airtable.com/&quot;&gt;Airtable&lt;/a&gt;, and a commercial ticketing system, and enterprise visualization software. Python makes it easy to wire these tools together in creative ways and fill gaps in functionality to solve many different kinds of problems.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;How have those tools changed the way you work? And how do you see them changing going forward? Will Python continue to play a role in the future of project management offices?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brian:&lt;/strong&gt; I think the most significant change is a sense of freedom, the ability to creatively solve problems, focus on what’s essential, and leverage modern solutions rather than being chained to tools, falling into tool ruts, or suffering from tool rot. As an added bonus, Python makes project management fun and refreshing.&lt;/p&gt;
&lt;p&gt;I’m not sure that Python will become mainstream in project management, but I do think it can give project managers who learn it a unique advantage in the automation of tedious tasks, solving complex problems, adapting to the evolution of organizations, and providing valuable outside-the-box PMO services.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Do you have any Python or tech related side projects you’re working on right now?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brian:&lt;/strong&gt; Yes, Road Maps to the Future is a project in partnership with a network of volunteers and in cooperation with agencies to help pilot it. With over 80,000 local governments in the United States alone, I believe there needs to be a better way to map out where we are, where we are going in the future, and the best ways to get there.&lt;/p&gt;
&lt;p&gt;In Python, we have an amazing assortment of tools for doing fantastic things with data, and we’re getting better at collecting discrete data.&lt;/p&gt;
&lt;p&gt;But there are opportunities in the exploration and non-linear collection of ideas, the identification of unseen problems, gaps in services, areas for improvement in the overall customer experience, the development of creative and viable alternative solutions to these problems, and then translating them into successful projects with demonstrable benefits.&lt;/p&gt;
&lt;p&gt;The road maps to the future are not just compilations of random ideas with new ways to collect information, but cohesive collections of core elements, processes, and partnerships necessary to propel the ideas into action through the organization of projects.&lt;/p&gt;
&lt;p&gt;To make it sustainable, it has to be discoverable, simple, quick and fun, so a lot of extra focus is being put into the UX side of the project. The current design uses Python, Vue.js, Azure Cosmos DB, Service Bus and Cognitive Services. It’s still in early development. Project information, details, and code will be released into the public domain next year.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;One of the reasons I was keen to interview you is because of your contribution to the Python community. You are one of two moderators in the &lt;a href=&quot;https://www.pythonistacafe.com/&quot;&gt;Pythonista Café&lt;/a&gt;, a peer-to-peer learning community for Python enthusiasts. How has your journey been from being a member to a moderator, and how has it influenced your Python chops?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brian:&lt;/strong&gt; It’s been great! &lt;a href=&quot;https://realpython.com/team/dbader/&quot;&gt;Dan Bader&lt;/a&gt; has done such a fantastic job, along with you, Jennifer (co-moderator), the &lt;em&gt;Real Python&lt;/em&gt; team, and especially all the amazing contributors from a wide range of backgrounds, geographical locations, and levels of experience who have made the Café into the lively forum it is today.&lt;/p&gt;
&lt;p&gt;It really is truly community-driven from the ground up. It has a fun, productive, open-source vibe without fear of public shaming. It’s okay to make mistakes and learn. You can post stuff without having to worry about how the whole world will interpret it or misinterpret it.&lt;/p&gt;
&lt;p&gt;I initially joined the Café for several reasons. It’s like a non-cliquey 24/7/365 Python meetup that you can drop in and out of at any time. As forum members have mentioned, it has a much better signal-to-noise ratio than the public forums, not to mention freedom from ads and social media algorithms that manipulate behaviors.&lt;/p&gt;
&lt;p&gt;It’s very welcoming and ultra-Python friendly. The passion for programming is seriously contagious, which leads to your question on how it has influenced my Python chops. The answer is that it has made Python even more fun, and I’m now even more committed and focused to continuous learning in Python and its ecosystem. It has also increased my excitement about the possibilities of what can be done with Python in the future.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Now for my last question. What other hobbies and interests do you have, aside from Python? Any you’d like to share and/or plug?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brian:&lt;/strong&gt; Outside of work and Python, I like to mix it up a lot. I currently work in the Panhandle and spend my off-time on the First Coast, so lately one of my favorite things to do is wandering around the south kayaking the many rivers and swamps; under certain conditions, it&amp;rsquo;s absolutely surreal. You can read and watch all you want about swamps, but there&amp;rsquo;s no substitute for the real thing.&lt;/p&gt;
&lt;p&gt;Each year I also pick out one or two new subjects to research and study for fun. I am an amateur radio operator, I love music, and I’m an avid reader of books, particularly history, survival stories, and science fiction.&lt;/p&gt;
&lt;p&gt;I’m also blessed to have an amazing wife and son. Outside of my projects, my wife keeps things lively with home projects, going to festivals and other events, helping animal rescues, along with exploring new areas and hiking with the dogs. Boredom is an alien concept&amp;mdash;it definitely keeps life fun.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Thank you, Brian, for joining me this week. If you&amp;rsquo;re looking for a friendly Python place to hang your metaphorical hat, you can &lt;a href=&quot;https://www.pythonistacafe.com/&quot;&gt;find the Pythonista Café here&lt;/a&gt;. Just be sure to say hi to Brian when you get there.&lt;/p&gt;
&lt;p&gt;Do you know an unsung hero in the Python community? If they&amp;rsquo;d like me to interview them in the future, I can be reached in the comments below, or you can &lt;a href=&quot;https://twitter.com/EndlessTrax&quot;&gt;send me a message on Twitter&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>Sending Emails With Python</title>
      <id>https://realpython.com/python-send-email/</id>
      <link href="https://realpython.com/python-send-email/"/>
      <updated>2018-12-05T14:00:00+00:00</updated>
      <summary>In this tutorial, you&#39;ll learn how to send emails using Python. Find out how to send plain-text and HTML messages, add files as attachments, and send personalized emails to multiple people.</summary>
      <content type="html">
        &lt;p&gt;You probably found this tutorial because you want to send emails using Python. Perhaps you want to receive email reminders from your code, send a confirmation email to users when they create an account, or send emails to members of your organization to remind them to pay their dues. Sending emails manually is a time-consuming and error-prone task, but it&amp;rsquo;s easy to automate with Python.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial you&amp;rsquo;ll learn how to&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Set up a &lt;strong&gt;secure connection&lt;/strong&gt; using &lt;code&gt;SMTP_SSL()&lt;/code&gt; and &lt;code&gt;.starttls()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use Python&amp;rsquo;s built-in &lt;code&gt;smtplib&lt;/code&gt; library to send &lt;strong&gt;basic emails&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Send emails with &lt;strong&gt;HTML content&lt;/strong&gt; and &lt;strong&gt;attachments&lt;/strong&gt; using the &lt;code&gt;email&lt;/code&gt; package&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Send multiple &lt;strong&gt;personalized emails&lt;/strong&gt; using a CSV file with contact data&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use the &lt;strong&gt;Yagmail&lt;/strong&gt; package to send email through your Gmail account using only a few lines of code&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&amp;rsquo;ll find a few transactional email services at the end of this tutorial, which will come in useful when you want to send a large number of emails. &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&quot;&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://realpython.com/installing-python/&quot;&gt;Python&lt;/a&gt; comes with the built-in &lt;a href=&quot;https://docs.python.org/3/library/smtplib.html&quot;&gt;&lt;code&gt;smtplib&lt;/code&gt;&lt;/a&gt; module for sending emails using the Simple Mail Transfer Protocol (SMTP). &lt;code&gt;smtplib&lt;/code&gt; uses the &lt;a href=&quot;https://tools.ietf.org/html/rfc821&quot;&gt;RFC 821&lt;/a&gt; protocol for SMTP. The examples in this tutorial will use the Gmail SMTP server to send emails, but the same principles apply to other email services. Although the majority of email providers use the same connection ports as the ones in this tutorial, you can run a quick &lt;a href=&quot;https://www.google.co.uk/search?&amp;amp;q=gmail+smtp+server+and+port&quot;&gt;Google search&lt;/a&gt; to confirm yours.&lt;/p&gt;
&lt;p&gt;To get started with this tutorial, &lt;a href=&quot;https://realpython.com/python-send-email/#option-1-setting-up-a-gmail-account-for-development&quot;&gt;set up a Gmail account for development&lt;/a&gt;, or &lt;a href=&quot;https://realpython.com/python-send-email/#option-2-setting-up-a-local-smtp-server&quot;&gt;set up an SMTP debugging server&lt;/a&gt; that discards emails you send and prints them to the command prompt instead. Both options are laid out for you below. A local SMTP debugging server can be useful for fixing any issues with email functionality and ensuring your email functions are bug-free before sending out any emails. &lt;/p&gt;
&lt;h3 id=&quot;option-1-setting-up-a-gmail-account-for-development&quot;&gt;Option 1: Setting up a Gmail Account for Development&lt;/h3&gt;
&lt;p&gt;If you decide to use a Gmail account to send your emails, I highly recommend setting up a throwaway account for the development of your code. This is because you&amp;rsquo;ll have to adjust your Gmail account&amp;rsquo;s security settings to allow access from your Python code, and because there&amp;rsquo;s a chance you might accidentally expose your login details. Also, I found that the inbox of my testing account rapidly filled up with test emails, which is reason enough to set up a new Gmail account for development.&lt;/p&gt;
&lt;p&gt;A nice feature of Gmail is that you can use the &lt;code&gt;+&lt;/code&gt; sign to add any modifiers to your email address, right before the &lt;code&gt;@&lt;/code&gt; sign. For example, mail sent to &lt;code&gt;my+person1@gmail.com&lt;/code&gt; and &lt;code&gt;my+person2@gmail.com&lt;/code&gt; will both arrive at &lt;code&gt;my@gmail.com&lt;/code&gt;. When testing email functionality, you can use this to emulate multiple addresses that all point to the same inbox.&lt;/p&gt;
&lt;p&gt;To set up a Gmail address for testing your code, do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://accounts.google.com/signup&quot;&gt;Create a new Google account&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Turn &lt;a href=&quot;https://myaccount.google.com/lesssecureapps&quot;&gt;&lt;em&gt;Allow less secure apps&lt;/em&gt; to &lt;em&gt;ON&lt;/em&gt;&lt;/a&gt;. Be aware that this makes it easier for others to gain access to your account. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don&amp;rsquo;t want to lower the security settings of your Gmail account, check out Google&amp;rsquo;s &lt;a href=&quot;https://developers.google.com/gmail/api/quickstart/python&quot;&gt;documentation&lt;/a&gt; on how to gain access credentials for your Python script, using the OAuth2 authorization framework.&lt;/p&gt;
&lt;h3 id=&quot;option-2-setting-up-a-local-smtp-server&quot;&gt;Option 2: Setting up a Local SMTP Server&lt;/h3&gt;
&lt;p&gt;You can test email functionality by running a local SMTP debugging server, using the &lt;code&gt;smptd&lt;/code&gt; module that comes pre-installed with Python. Rather than sending emails to the specified address, it discards them and prints their content to the console. Running a local debugging server means it&amp;rsquo;s not necessary to deal with encryption of messages or use credentials to log in to an email server.&lt;/p&gt;
&lt;p&gt;You can start a local SMTP debugging server by typing the following in Command Prompt:&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 -m smtpd -c DebuggingServer -n localhost:1025
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On Linux, use the same command preceded by &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Any emails sent through this server will be discarded and shown in the terminal window as a &lt;a href=&quot;https://docs.python.org/3/library/stdtypes.html#bytes-objects&quot;&gt;&lt;code&gt;bytes&lt;/code&gt;&lt;/a&gt; object for each line:&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;---------- MESSAGE FOLLOWS ----------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;b&amp;#39;X-Peer: ::1&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;b&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;b&amp;#39;From: my@address.com&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;b&amp;#39;To: your@address.com&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;b&amp;#39;Subject: a local test mail&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;b&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;b&amp;#39;Hello there, here is a test email&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;------------ END MESSAGE ------------&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For the rest of the tutorial, I&amp;rsquo;ll assume you&amp;rsquo;re using a Gmail account, but if you&amp;rsquo;re using a local debugging server, just make sure to use &lt;code&gt;localhost&lt;/code&gt; as your SMTP server and use port 1025 rather than port 465 or 587. Besides this, you won&amp;rsquo;t need to use &lt;code&gt;login()&lt;/code&gt; or encrypt the communication using SSL/TLS.&lt;/p&gt;
&lt;h2 id=&quot;sending-a-plain-text-email&quot;&gt;Sending a Plain-Text Email&lt;/h2&gt;
&lt;p&gt;Before we dive into sending emails with HTML content and attachments, you&amp;rsquo;ll learn to send plain-text emails using Python. These are emails that you could write up in a simple text editor. There&amp;rsquo;s no fancy stuff like text formatting or hyperlinks. You&amp;rsquo;ll learn that a bit later.&lt;/p&gt;
&lt;h3 id=&quot;starting-a-secure-smtp-connection&quot;&gt;Starting a Secure SMTP Connection&lt;/h3&gt;
&lt;p&gt;When you send emails through Python, you should make sure that your SMTP connection is encrypted, so that your message and login credentials are not easily accessed by others. SSL (Secure Sockets Layer) and TLS (Transport Layer Security) are two protocols that can be used to encrypt an SMTP connection. It&amp;rsquo;s not necessary to use either of these when using a local debugging server.&lt;/p&gt;
&lt;p&gt;There are two ways to start a secure connection with your email server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start an SMTP connection that is secured from the beginning using &lt;code&gt;SMTP_SSL()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Start an unsecured SMTP connection that can then be encrypted using &lt;code&gt;.starttls()&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In both instances, Gmail will encrypt emails using TLS, as this is the more secure successor of SSL. As per Python&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/3/library/ssl.html#ssl-security&quot;&gt;Security considerations&lt;/a&gt;, it is highly recommended that you use &lt;code&gt;create_default_context()&lt;/code&gt; from the &lt;a href=&quot;https://docs.python.org/3/library/ssl.html&quot;&gt;&lt;code&gt;ssl&lt;/code&gt;&lt;/a&gt; module. This will load the system&amp;rsquo;s trusted CA certificates, enable host name checking and certificate validation, and try to choose reasonably secure protocol and cipher settings.&lt;/p&gt;
&lt;p&gt;If you want to check the encryption for an email in your Gmail inbox, go to &lt;em&gt;More&lt;/em&gt; → &lt;em&gt;Show original&lt;/em&gt; to see the encryption type listed under the &lt;em&gt;Received&lt;/em&gt; header.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.python.org/3/library/smtplib.html&quot;&gt;&lt;code&gt;smtplib&lt;/code&gt;&lt;/a&gt; is Python&amp;rsquo;s built-in module for sending emails to any Internet machine with an SMTP or ESMTP listener daemon. &lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll show you how to use &lt;code&gt;SMTP_SSL()&lt;/code&gt; first, as it instantiates a connection that is secure from the outset and is slightly more concise than the &lt;code&gt;.starttls()&lt;/code&gt; alternative. Keep in mind that Gmail requires that you connect to port 465 if using &lt;code&gt;SMTP_SSL()&lt;/code&gt;, and to port 587 when using &lt;code&gt;.starttls()&lt;/code&gt;. &lt;/p&gt;
&lt;h4 id=&quot;option-1-using-smtp_ssl&quot;&gt;Option 1: Using &lt;code&gt;SMTP_SSL()&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;The code example below creates a secure connection with Gmail&amp;rsquo;s SMTP server, using the &lt;code&gt;SMTP_SSL()&lt;/code&gt; of &lt;code&gt;smtplib&lt;/code&gt; to initiate a TLS-encrypted connection. The default context of &lt;code&gt;ssl&lt;/code&gt; validates the host name and its certificates and optimizes the security of the connection. Make sure to fill in your own email address instead of &lt;code&gt;my@gmail.com&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ssl&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;465&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# For SSL&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;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;Type your password and press enter: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a secure SSL context&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_default_context&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMTP_SSL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;smtp.gmail.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&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;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&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;c1&quot;&gt;# TODO: Send email here&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;with smtplib.SMTP_SSL() as server:&lt;/code&gt; makes sure that the connection is automatically closed at the end of the indented code block. If &lt;code&gt;port&lt;/code&gt; is zero, or not specified, &lt;code&gt;.SMTP_SSL()&lt;/code&gt; will use the standard port for SMTP over SSL (port 465).&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not safe practice to store your email password in your code, especially if you intend to share it with others. Instead, use &lt;code&gt;input()&lt;/code&gt; to let the user type in their password when running the script, as in the example above. If you don&amp;rsquo;t want your password to show on your screen when you type it, you can import the &lt;a href=&quot;https://docs.python.org/3/library/getpass.html&quot;&gt;&lt;code&gt;getpass&lt;/code&gt;&lt;/a&gt; module and use &lt;code&gt;.getpass()&lt;/code&gt; instead for blind input of your password.&lt;/p&gt;
&lt;h4 id=&quot;option-2-using-starttls&quot;&gt;Option 2: Using &lt;code&gt;.starttls()&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Instead of using &lt;code&gt;.SMTP_SSL()&lt;/code&gt; to create a connection that is secure from the outset, we can create an unsecured SMTP connection and encrypt it using &lt;code&gt;.starttls()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To do this, create an instance of &lt;code&gt;smtplib.SMTP&lt;/code&gt;, which encapsulates an SMTP connection and allows you access to its methods. I recommend defining your SMTP server and port at the beginning of your script to configure them easily.&lt;/p&gt;
&lt;p&gt;The code snippet below uses the construction &lt;code&gt;server = SMTP()&lt;/code&gt;, rather than the format &lt;code&gt;with SMTP() as server:&lt;/code&gt; which we used in the previous example. To make sure that your code doesn&amp;rsquo;t crash when something goes wrong, put your main code in a &lt;code&gt;try&lt;/code&gt; block, and let an &lt;code&gt;except&lt;/code&gt; block print any error messages to &lt;code&gt;stdout&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ssl&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;smtp_server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;smtp.gmail.com&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;587&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# For starttls&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&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;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;Type your password and press enter: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a secure SSL context&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_default_context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Try to log in to server and send email&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMTP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;smtp_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ehlo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Can be omitted&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;starttls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Secure the connection&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ehlo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Can be omitted&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender_email&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;c1&quot;&gt;# TODO: Send email here&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Print any error messages to stdout&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;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To identify yourself to the server, &lt;code&gt;.helo()&lt;/code&gt; (SMTP) or &lt;code&gt;.ehlo()&lt;/code&gt; (ESMTP) should be called after creating an &lt;code&gt;.SMTP()&lt;/code&gt; object, and again after &lt;code&gt;.starttls()&lt;/code&gt;. This function is implicitly called by &lt;code&gt;.starttls()&lt;/code&gt; and &lt;code&gt;.sendmail()&lt;/code&gt; if needed, so unless you want to check the SMTP service extensions of the server, it is not necessary to use &lt;code&gt;.helo()&lt;/code&gt; or &lt;code&gt;.ehlo()&lt;/code&gt; explicitly.&lt;/p&gt;
&lt;h3 id=&quot;sending-your-plain-text-email&quot;&gt;Sending Your Plain-text Email&lt;/h3&gt;
&lt;p&gt;After you initiated a secure SMTP connection using either of the above methods, you can send your email using &lt;code&gt;.sendmail()&lt;/code&gt;, which pretty much does what it says on the tin:&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;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sendmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;receiver_email&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;I recommend defining the email addresses and message content at the top of your script, after the imports, so you can change them easily:&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;sender_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;receiver_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;your@gmail.com&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;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;Subject: Hi there&lt;/span&gt;

&lt;span class=&quot;s2&quot;&gt;This message is sent from Python.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Send email here&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;message&lt;/code&gt; &lt;a href=&quot;https://realpython.com/python-strings/&quot;&gt;string&lt;/a&gt; starts with &lt;code&gt;&quot;Subject: Hi there&quot;&lt;/code&gt; followed by two newlines (&lt;code&gt;\n&lt;/code&gt;). This ensures &lt;code&gt;Hi there&lt;/code&gt; shows up as the subject of the email, and the text following the newlines will be treated as the message body. &lt;/p&gt;
&lt;p&gt;The code example below sends a plain-text email using &lt;code&gt;SMTP_SSL()&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ssl&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;465&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# For SSL&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;smtp_server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;smtp.gmail.com&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Enter your address&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;receiver_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;your@gmail.com&amp;quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Enter receiver address&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;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;Type your password and press enter: &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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;Subject: Hi there&lt;/span&gt;

&lt;span class=&quot;s2&quot;&gt;This message is sent from Python.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_default_context&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMTP_SSL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;smtp_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&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;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender_email&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;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sendmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;receiver_email&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;For comparison, here is a code example that sends a plain-text email over an SMTP connection secured with &lt;code&gt;.starttls()&lt;/code&gt;. The &lt;code&gt;server.ehlo()&lt;/code&gt; lines may be omitted, as they are called implicitly by &lt;code&gt;.starttls()&lt;/code&gt; and &lt;code&gt;.sendmail()&lt;/code&gt;, if required:&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ssl&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;587&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# For starttls&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;smtp_server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;smtp.gmail.com&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;receiver_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;your@gmail.com&amp;quot;&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;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;Type your password and press enter:&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;Subject: Hi there&lt;/span&gt;

&lt;span class=&quot;s2&quot;&gt;This message is sent from Python.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_default_context&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMTP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;smtp_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&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;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ehlo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Can be omitted&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;starttls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ehlo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Can be omitted&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender_email&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;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sendmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;receiver_email&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;h2 id=&quot;sending-fancy-emails&quot;&gt;Sending Fancy Emails&lt;/h2&gt;
&lt;p&gt;Python&amp;rsquo;s built-in &lt;code&gt;email&lt;/code&gt; package allows you to  structure more fancy emails, which can then be transferred with &lt;code&gt;smptlib&lt;/code&gt; as you have done already. Below, you&amp;rsquo;ll learn how use the &lt;code&gt;email&lt;/code&gt; package to send emails with HTML content and attachments.&lt;/p&gt;
&lt;h3 id=&quot;including-html-content&quot;&gt;Including HTML Content&lt;/h3&gt;
&lt;p&gt;If you want to format the text in your email (&lt;strong&gt;bold&lt;/strong&gt;, &lt;em&gt;italics&lt;/em&gt;, and so on), or if you want to add any images, hyperlinks, or responsive content, then HTML comes in very handy. Today&amp;rsquo;s most common type of email is the MIME (Multipurpose Internet Mail Extensions) Multipart email, combining HTML and plain-text. MIME messages are handled by Python&amp;rsquo;s &lt;code&gt;email.mime&lt;/code&gt; module. For a detailed description, check &lt;a href=&quot;https://docs.python.org/3/library/email.mime.html&quot;&gt;the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As not all email clients display HTML content by default, and some people choose only to receive plain-text emails for security reasons, it is important to include a plain-text alternative for HTML messages. As the email client will render the last multipart attachment first, make sure to add the HTML message after the plain-text version.&lt;/p&gt;
&lt;p&gt;In the example below, our &lt;code&gt;MIMEText()&lt;/code&gt; objects will contain the HTML and plain-text versions of our message, and the &lt;code&gt;MIMEMultipart(&quot;alternative&quot;)&lt;/code&gt; instance combines these into a single message with two alternative rendering options:&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ssl&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;email.mime.text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MIMEText&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;email.mime.multipart&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MIMEMultipart&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;receiver_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;your@gmail.com&amp;quot;&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;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;Type your password and press enter:&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MIMEMultipart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;alternative&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;s2&quot;&gt;&amp;quot;Subject&amp;quot;&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;s2&quot;&gt;&amp;quot;multipart test&amp;quot;&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;From&amp;quot;&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;sender_email&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;To&amp;quot;&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;receiver_email&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create the plain-text and HTML version of your message&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;Hi,&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;How are you?&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;Real Python has many great tutorials:&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;www.realpython.com&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;  &amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;    &amp;lt;p&amp;gt;Hi,&amp;lt;br&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;       How are you?&amp;lt;br&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;       &amp;lt;a href=&amp;quot;http://www.realpython.com&amp;quot;&amp;gt;Real Python&amp;lt;/a&amp;gt; &lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;       has many great tutorials.&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;    &amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;  &amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Turn these into plain/html MIMEText objects&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;part1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MIMEText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;part2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MIMEText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;html&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add HTML/plain-text parts to MIMEMultipart message&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# The email client will try to render the last part first&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;attach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part1&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;attach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create secure connection with server and send email&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_default_context&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMTP_SSL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;smtp.gmail.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;465&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&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;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender_email&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;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sendmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;receiver_email&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;as_string&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;In this example, you first define the plain-text and HTML message as string literals, and then store them as &lt;code&gt;plain&lt;/code&gt;/&lt;code&gt;html&lt;/code&gt; &lt;code&gt;MIMEText&lt;/code&gt; objects. These can then be added in this order to the &lt;code&gt;MIMEMultipart(&quot;alternative&quot;)&lt;/code&gt; message and sent through your secure connection with the email server. Remember to add the HTML message after the plain-text alternative, as email clients will try to render the last subpart first.&lt;/p&gt;
&lt;h3 id=&quot;adding-attachments-using-the-email-package&quot;&gt;Adding Attachments Using the &lt;code&gt;email&lt;/code&gt; Package&lt;/h3&gt;
&lt;p&gt;In order to send binary files to an email server that is designed to work with textual data, they need to be encoded before transport. This is most commonly done using &lt;a href=&quot;https://docs.python.org/3/library/base64.html&quot;&gt;&lt;code&gt;base64&lt;/code&gt;&lt;/a&gt;, which encodes binary data into printable ASCII characters.&lt;/p&gt;
&lt;p&gt;The code example below shows how to send an email with a PDF file as an attachment:&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;email&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ssl&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoders&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;email.mime.base&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MIMEBase&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;email.mime.multipart&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MIMEMultipart&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;email.mime.text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MIMEText&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;An email with attachment from Python&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;This is an email with attachment sent from Python&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;receiver_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;your@gmail.com&amp;quot;&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;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;Type your password and press enter:&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a multipart message and set headers&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;MIMEMultipart&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;From&amp;quot;&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;sender_email&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;To&amp;quot;&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;receiver_email&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;Subject&amp;quot;&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;subject&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;Bcc&amp;quot;&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;receiver_email&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Recommended for mass emails&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add body to email&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;attach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MIMEText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;document.pdf&amp;quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# In same directory as script&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Open PDF file in binary mode&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;filename&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;attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Add file as application/octet-stream&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Email client can usually download this automatically as attachment&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MIMEBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;application&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;octet-stream&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attachment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Encode file in ASCII characters to send by email    &lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;encoders&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode_base64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add header as key/value pair to attachment part&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;Content-Disposition&amp;quot;&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;attachment; filename= &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{filename}&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;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add attachment to message and convert message to string&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;attach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;text&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;as_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Log in to server using secure context and send email&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_default_context&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMTP_SSL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;smtp.gmail.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;465&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&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;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender_email&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;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sendmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;receiver_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&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;MIMEultipart()&lt;/code&gt; message accepts parameters in the form of &lt;a href=&quot;https://tools.ietf.org/html/rfc5233.html&quot;&gt;RFC5233&lt;/a&gt;-style key/value pairs, which are stored in a dictionary and passed to the &lt;a href=&quot;https://docs.python.org/2/library/email.message.html#email.message.Message.add_header&quot;&gt;&lt;code&gt;.add_header&lt;/code&gt; method&lt;/a&gt; of the &lt;a href=&quot;https://docs.python.org/3/library/email.compat32-message.html#module-email.message&quot;&gt;&lt;code&gt;Message&lt;/code&gt;&lt;/a&gt; base class.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://docs.python.org/3/library/email.mime.html&quot;&gt;documentation&lt;/a&gt; for Python&amp;rsquo;s &lt;code&gt;email.mime&lt;/code&gt; module to learn more about using MIME classes.  &lt;/p&gt;
&lt;h2 id=&quot;sending-multiple-personalized-emails&quot;&gt;Sending Multiple Personalized Emails&lt;/h2&gt;
&lt;p&gt;Imagine you want to send emails to members of your organization, to remind them to pay their contribution fees. Or maybe you want to send students in your class personalized emails with the grades for their recent assignment. These tasks are a breeze in Python.&lt;/p&gt;
&lt;h3 id=&quot;make-a-csv-file-with-relevant-personal-info&quot;&gt;Make a CSV File With Relevant Personal Info&lt;/h3&gt;
&lt;p&gt;An easy starting point for sending multiple personalized emails is to &lt;a href=&quot;https://realpython.com/python-csv/&quot;&gt;create a CSV (comma-separated values) file&lt;/a&gt; that contains all the required personal information. (Make sure not to share other people&amp;rsquo;s private information without their consent.) A CSV file can be thought of as a simple table, where the first line often contains the column headers. &lt;/p&gt;
&lt;p&gt;Below are the contents of the file &lt;code&gt;contacts_file.csv&lt;/code&gt;, which I saved in the same folder as my Python code. It contains the names, addresses, and grades for a set of fictional people. I used &lt;code&gt;my+modifier@gmail.com&lt;/code&gt; constructions to make sure all emails end up in my own inbox, which in this example is &lt;a href=&quot;&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#116;&amp;#111;&amp;#58;&amp;#109;&amp;#121;&amp;#64;&amp;#103;&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#46;&amp;#99;&amp;#111;&amp;#109;&quot;&gt;&amp;#109;&amp;#121;&amp;#64;&amp;#103;&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#46;&amp;#99;&amp;#111;&amp;#109;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight csv&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;name,email,grade
Ron Obvious,my+ovious@gmail.com,B+
Killer Rabbit of Caerbannog,my+rabbit@gmail.com,A
Brian Cohen,my+brian@gmail.com,C
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When creating a CSV file, make sure to separate your values by a comma, without any surrounding whitespaces.&lt;/p&gt;
&lt;h3 id=&quot;loop-over-rows-to-send-multiple-emails&quot;&gt;Loop Over Rows to Send Multiple Emails&lt;/h3&gt;
&lt;p&gt;The code example below shows you how to open a CSV file and loop over its lines of content (skipping the header row). To make sure that the code works correctly before you send emails to all your contacts, I&amp;rsquo;ve printed &lt;code&gt;Sending email to ...&lt;/code&gt; for each contact, which we can later replace with functionality that actually sends out emails:&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;csv&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;s2&quot;&gt;&amp;quot;contacts_file.csv&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;reader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Skip header row&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;email&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;reader&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;s2&quot;&gt;&amp;quot;Sending email to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&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;# Send email here&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the example above, using &lt;code&gt;with open(filename) as file:&lt;/code&gt;makes sure that your file closes at the end of the code block. &lt;code&gt;csv.reader()&lt;/code&gt; makes it easy to read a CSV file line by line and extract its values. The &lt;code&gt;next(reader)&lt;/code&gt; line skips the header row, so that the following line &lt;code&gt;for name, email, grade in reader:&lt;/code&gt; splits subsequent rows at each comma, and stores the resulting values in the strings &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;grade&lt;/code&gt; for the current contact. &lt;/p&gt;
&lt;p&gt;If the values in your CSV file contain whitespaces on either or both sides, you can remove them using the &lt;code&gt;.strip()&lt;/code&gt; method.&lt;/p&gt;
&lt;h3 id=&quot;personalized-content&quot;&gt;Personalized Content&lt;/h3&gt;
&lt;p&gt;You can put personalized content in a message by using &lt;a href=&quot;https://realpython.com/python-string-formatting/&quot;&gt;&lt;code&gt;str.format()&lt;/code&gt;&lt;/a&gt; to fill in curly-bracket placeholders.
For example, &lt;code&gt;&quot;hi {name}, you {result} your assignment&quot;.format(name=&quot;John&quot;, result=&quot;passed&quot;)&lt;/code&gt; will give you &lt;code&gt;&quot;hi John, you passed your assignment&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As of Python 3.6, string formatting can be done more elegantly using &lt;a href=&quot;https://realpython.com/python-f-strings/&quot;&gt;f-strings&lt;/a&gt;, but these require the placeholders to be defined before the f-string itself. In order to define the email message at the beginning of the script, and fill in placeholders for each contact when looping over the CSV file, the older &lt;code&gt;.format()&lt;/code&gt; method is used.&lt;/p&gt;
&lt;p&gt;With this in mind, you can set up a general message body, with placeholders that can be tailored to individuals.&lt;/p&gt;
&lt;h3 id=&quot;code-example&quot;&gt;Code Example&lt;/h3&gt;
&lt;p&gt;The following code example lets you send personalized emails to multiple contacts. It loops over a CSV file with &lt;code&gt;name,email,grade&lt;/code&gt; for each contact, as in the &lt;a href=&quot;https://realpython.com/python-send-email/#make-a-csv-file-with-relevant-personal-info&quot;&gt;example above&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The general message is defined in the beginning of the script, and for each contact in the CSV file its &lt;code&gt;{name}&lt;/code&gt; and &lt;code&gt;{grade}&lt;/code&gt; placeholders are filled in, and a personalized email is sent out through a secure connection with the Gmail server, as you saw before:&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;csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ssl&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;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Subject: Your grade&lt;/span&gt;

&lt;span class=&quot;s2&quot;&gt;Hi &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, your grade is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{grade}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;from_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&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;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;Type your password and press enter: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_default_context&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;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMTP_SSL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;smtp.gmail.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;465&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&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;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_address&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;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;s2&quot;&gt;&amp;quot;contacts_file.csv&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;reader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Skip header row&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;email&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;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sendmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;from_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;email&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;format&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;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;o&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;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;yagmail&quot;&gt;Yagmail&lt;/h2&gt;
&lt;p&gt;There are multiple libraries designed to make sending emails easier, such as &lt;a href=&quot;https://github.com/tomekwojcik/envelopes&quot;&gt;Envelopes&lt;/a&gt;, &lt;a href=&quot;https://github.com/mailgun/flanker&quot;&gt;Flanker&lt;/a&gt; and &lt;a href=&quot;https://pypi.org/project/yagmail/&quot;&gt;Yagmail&lt;/a&gt;. Yagmail is designed to work specifically with Gmail, and it greatly simplifies the process of sending emails through a friendly API, as you can see in the code example 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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;yagmail&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;receiver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;your@gmail.com&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Hello there from Yagmail&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;document.pdf&amp;quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;yag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yagmail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMTP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;yag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Yagmail test with attachment&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;attachments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&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;This code example sends an email with a PDF attachment in a fraction of the lines needed for our &lt;a href=&quot;https://realpython.com/python-send-email/#adding-attachments-using-the-email-package&quot;&gt;example using &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;smtplib&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When setting up Yagmail, you can add your Gmail validations to the keyring of your OS, as described in &lt;a href=&quot;https://yagmail.readthedocs.io/en/latest/api.html#authentication&quot;&gt;the documentation&lt;/a&gt;. If you don&amp;rsquo;t do this, Yagmail will prompt you to enter your password when required and store it in the keyring automatically.&lt;/p&gt;
&lt;h2 id=&quot;transactional-email-services&quot;&gt;Transactional Email Services&lt;/h2&gt;
&lt;p&gt;If you plan to send a large volume of emails, want to see email statistics, and want to ensure reliable delivery, it may be worth looking into transactional email services.
Although all of the following services have paid plans for sending large volumes of emails, they also come with a free plan so you can try them out. Some of these free plans are valid indefinitely and may be sufficient for your email needs.&lt;/p&gt;
&lt;p&gt;Below is an overview of the free plans for some of the major transactional email services. Clicking on the provider name will take you to the pricing section of their website.&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;Provider&lt;/th&gt;
&lt;th&gt;Free plan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://sendgrid.com/marketing/sendgrid-services-cro/#compare-plans&quot;&gt;Sendgrid&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;40,000 emails for your first 30 days, then 100/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.sendinblue.com/pricing/&quot;&gt;Sendinblue&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;300 emails/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.mailgun.com/pricing-simple&quot;&gt;Mailgun&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;First 10,000 emails free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.mailjet.com/pricing/&quot;&gt;Mailjet&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;200 emails/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/free/?awsf.Free%20Tier%20Types=categories%23alwaysfree&quot;&gt;Amazon SES&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;62,000 emails/month&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;You can run a &lt;a href=&quot;https://www.google.co.uk/search?q=transactional+email+providers+comparison&quot;&gt;Google search&lt;/a&gt; to see which provider best fits your needs, or just try out a few of the free plans to see which API you like working with most.&lt;/p&gt;
&lt;h2 id=&quot;sendgrid-code-example&quot;&gt;Sendgrid Code Example&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a code example for sending emails with &lt;a href=&quot;https://sendgrid.com/marketing/sendgrid-services-cro/#compare-plans&quot;&gt;Sendgrid&lt;/a&gt; to give you a flavor of how to use a transactional email service with Python:&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;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sendgrid&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sendgrid.helpers.mail&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mail&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sendgrid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SendGridAPIClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;apikey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&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;s2&quot;&gt;&amp;quot;SENDGRID_API_KEY&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;from_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;my@gmail.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;to_email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;your@gmail.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;A test email from Sendgrid&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Here&amp;#39;s a test email sent through Python&amp;quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mail&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;c1&quot;&gt;# The statements below can be included for debugging 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;response&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;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;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&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;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To run this code, you must first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://sendgrid.com/free/?source=sendgrid-python&quot;&gt;Sign up for a (free) Sendgrid account&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://app.sendgrid.com/settings/api_keys&quot;&gt;Request an API key&lt;/a&gt; for user validation&lt;/li&gt;
&lt;li&gt;Add your API key by typing &lt;code&gt;setx SENDGRID_API_KEY &quot;YOUR_API_KEY&quot;&lt;/code&gt; in Command Prompt (to store this API key permanently) or &lt;code&gt;set SENDGRID_API_KEY YOUR_API_KEY&lt;/code&gt; to store it only for the current client session&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More information on how to set up Sendgrid for Mac and Windows can be found in the repository&amp;rsquo;s README on &lt;a href=&quot;https://github.com/sendgrid/sendgrid-python&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can now start a secure SMTP connection and send multiple personalized emails to the people in your contacts list!&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ve learned how to send an HTML email with a plain-text alternative and attach files to your emails. The &lt;a href=&quot;https://pypi.org/project/yagmail/&quot;&gt;Yagmail&lt;/a&gt; package simplifies all these tasks when you&amp;rsquo;re using a Gmail account. If you plan to send large volumes of email, it is worth looking into transactional email services.&lt;/p&gt;
&lt;p&gt;Enjoy sending emails with Python, and remember: &lt;a href=&quot;https://www.youtube.com/watch?v=UO7HY7Nz398&quot;&gt;no spam please&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>Building Serverless Python Apps Using AWS Chalice</title>
      <id>https://realpython.com/aws-chalice-serverless-python/</id>
      <link href="https://realpython.com/aws-chalice-serverless-python/"/>
      <updated>2018-12-03T14:00:00+00:00</updated>
      <summary>In this Python tutorial, you&#39;ll see just how easy it can be to get your serverless apps up and running! Chalice, a Python Serverless Microframework developed by AWS, enables you to quickly spin up and deploy a working serverless app that scales up and down on its own as required using AWS Lambda.</summary>
      <content type="html">
        &lt;p&gt;Shipping a web application usually involves having your code up and running on single or multiple servers. In this model, you end up setting up processes for monitoring, provisioning, and scaling your servers up or down. Although this seems to work well, having all the logistics around a web application handled in an automated manner reduces a lot of manual overhead. Enter Serverless.&lt;/p&gt;
&lt;p&gt;With &lt;a href=&quot;https://aws.amazon.com/lambda/serverless-architectures-learn-more/&quot;&gt;Serverless Architecture&lt;/a&gt;, you don&amp;rsquo;t manage servers. Instead, you only need to ship the code or the executable package to the platform that executes it. It&amp;rsquo;s not really serverless. The servers do exist, but the developer doesn&amp;rsquo;t need to worry about them.&lt;/p&gt;
&lt;p&gt;AWS introduced &lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/welcome.html&quot;&gt;Lambda Services&lt;/a&gt;, a platform that enables developers to simply have their code executed in a particular runtime environment. To make the platform easy to use, many communities have come up with some really good frameworks around it in order to make the serverless apps a working solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this tutorial, you&amp;rsquo;ll be able to&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Discuss the benefits of a serverless architecture&lt;/li&gt;
&lt;li&gt;Explore Chalice, a Python serverless framework&lt;/li&gt;
&lt;li&gt;Build a full blown serverless app for a real world use case&lt;/li&gt;
&lt;li&gt;Deploy to Amazon Web Services (AWS) Lambda&lt;/li&gt;
&lt;li&gt;Compare Pure and Lambda functions&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;getting-started-with-aws-chalice&quot;&gt;Getting Started With AWS Chalice&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/aws/chalice/&quot;&gt;Chalice&lt;/a&gt;, a Python Serverless Microframework developed by AWS, enables you to quickly spin up and deploy a working serverless app that scales up and down on its own as required using AWS Lambda.&lt;/p&gt;
&lt;h3 id=&quot;why-chalice&quot;&gt;Why Chalice?&lt;/h3&gt;
&lt;p&gt;For Python developers accustomed to the Flask web framework, Chalice should be a breeze in terms of building and shipping your first app. Highly inspired by Flask, Chalice keeps it pretty minimalist in terms of defining what the service should be like and finally making an executable package of the same.&lt;/p&gt;
&lt;p&gt;Enough theory! Let&amp;rsquo;s start with a basic &lt;code&gt;hello-world&lt;/code&gt; app and kick-start our serverless journey.&lt;/p&gt;
&lt;h3 id=&quot;project-setup&quot;&gt;Project Setup&lt;/h3&gt;
&lt;p&gt;Before diving into Chalice, you&amp;rsquo;ll set up a working environment on your local machine, which will set you up for the rest of the tutorial.&lt;/p&gt;
&lt;p&gt;First, create and activate a virtual environment and install Chalice:&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 -m venv env
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; env/bin/activate
&lt;span class=&quot;gp&quot;&gt;(env)$&lt;/span&gt; pip install chalice
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Follow our comprehensive guide on the &lt;a href=&quot;https://realpython.com/pipenv-guide/&quot;&gt;Pipenv packaging tool&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; Chalice comes with a user-friendly CLI that makes it easy to play around with your serverless app.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now that you have Chalice installed on your virtual environment, let&amp;rsquo;s use the Chalice CLI to generate some boilerplate 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;(env)$&lt;/span&gt; chalice new-project
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Enter the name of the project when prompted and hit return. A new directory is created with that name:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&amp;lt;project-name&amp;gt;/
|
├── .chalice/
│   └── config.json
|
├── .gitignore
├── app.py
└── requirements.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;See how minimalist the Chalice codebase is. A &lt;code&gt;.chalice&lt;/code&gt; directory, &lt;code&gt;app.py&lt;/code&gt;, and &lt;code&gt;requirements.txt&lt;/code&gt; is all that it requires to have a serverless app up and running. Let&amp;rsquo;s quickly run the app on our local machine.&lt;/p&gt;
&lt;p&gt;Chalice CLI consists of really great utility functions allowing you to perform a number of operations from running locally to deploying in a Lambda environment.&lt;/p&gt;
&lt;h3 id=&quot;build-and-run-locally&quot;&gt;Build and Run Locally&lt;/h3&gt;
&lt;p&gt;You can simulate the app by running it locally using the &lt;code&gt;local&lt;/code&gt; utility of Chalice:&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;(env)$&lt;/span&gt; chalice &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Serving on 127.0.0.1:8000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By default, Chalice runs on port 8000. We can now check the index route by making a &lt;a href=&quot;http://www.codingpedia.org/ama/how-to-test-a-rest-api-from-command-line-with-curl/&quot;&gt;curl request&lt;/a&gt; to &lt;code&gt;http://localhost:8000/&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; curl -X GET http://localhost:8000/
&lt;span class=&quot;go&quot;&gt;{&amp;quot;hello&amp;quot;: &amp;quot;world&amp;quot;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now if we look at &lt;code&gt;app.py&lt;/code&gt;, we can appreciate the simplicity with which Chalice allows you to build a serverless service. All the complex stuff is handled by the decorators:&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;chalice&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chalice&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;Chalice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;serverless-sms-service&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/&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;index&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;world&amp;#39;&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;: We haven&amp;rsquo;t named our app &lt;code&gt;hello-world&lt;/code&gt;, as we will build our SMS service on the same app.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now, let&amp;rsquo;s move on to deploying our app on the AWS Lambda.&lt;/p&gt;
&lt;h3 id=&quot;deploy-on-aws-lambda&quot;&gt;Deploy on AWS Lambda&lt;/h3&gt;
&lt;p&gt;Chalice makes deploying your serverless app completely effortless. Using the &lt;code&gt;deploy&lt;/code&gt; utility, you can simply instruct Chalice to deploy and create a Lambda function that can be accessible via a REST API.&lt;/p&gt;
&lt;p&gt;Before we begin deployment, we need to make sure we have our AWS credentials in place, usually located at &lt;code&gt;~/.aws/config&lt;/code&gt;. The contents of the file look as follows:&lt;/p&gt;
&lt;div class=&quot;highlight ini&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;[default]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;aws_access_key_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;your-access-key-id&amp;gt;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;aws_secret_access_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;your-secret-access-key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;your-region&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With AWS credentials in place, let&amp;rsquo;s begin our deployment process with just 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;(env)$&lt;/span&gt; chalice deploy
&lt;span class=&quot;go&quot;&gt;Creating deployment package.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Updating policy for IAM role: hello-world-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Creating lambda function: hello-world-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Creating Rest API&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Resources deployed:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - Lambda ARN: arn:aws:lambda:ap-south-1:679337104153:function:hello-world-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - Rest API URL: https://fqcdyzvytc.execute-api.ap-south-1.amazonaws.com/api/&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;: The generated ARN and API URL in the above snippet will vary from user to user.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Wow! Yes, it is really this easy to get your serverless app up and running. To verify simply make a curl request on the generated Rest API URL:&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 -X GET https://fqcdyzvytc.execute-api.ap-south-1.amazonaws.com/api/
&lt;span class=&quot;go&quot;&gt;{&amp;quot;hello&amp;quot;: &amp;quot;world&amp;quot;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Typically, this is all that you need to get your serverless app up and running. You can also go to your AWS console and see the Lambda function created under the Lambda service section. Each Lambda service has a unique REST API endpoint that can be consumed in any web application.&lt;/p&gt;
&lt;p&gt;Next, you will begin building your Serverless SMS Sender service using Twilio as an SMS service provider.&lt;/p&gt;
&lt;h2 id=&quot;building-a-serverless-sms-sender-service&quot;&gt;Building a Serverless SMS Sender Service&lt;/h2&gt;
&lt;p&gt;With a basic &lt;code&gt;hello-world&lt;/code&gt; app deployed, let&amp;rsquo;s move on to building a more real-world application that can be used along with everyday web apps. In this section, you&amp;rsquo;ll build a completely serverless SMS-sending app that can be plugged into any system and work as expected as long as the input parameters are correct.&lt;/p&gt;
&lt;p&gt;In order to send SMS, we will be using &lt;a href=&quot;https://www.twilio.com&quot;&gt;Twilio&lt;/a&gt;, a developer-friendly SMS service. Before we begin using Twilio, we need to take care of a few prerequisites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create an account and acquire &lt;code&gt;ACCOUNT_SID&lt;/code&gt; and &lt;code&gt;AUTH_TOKEN&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Get a mobile phone number, which is  available for free at Twilio for minor testing stuff.&lt;/li&gt;
&lt;li&gt;Install the &lt;code&gt;twilio&lt;/code&gt; package in our virtual environment using &lt;code&gt;pip install twilio&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With all the above prerequisites checked, you can start building your SMS service client using Twilio&amp;rsquo;s Python library. Let&amp;rsquo;s begin by cloning the &lt;a href=&quot;https://github.com/realpython/materials/pull/16&quot;&gt;repository&lt;/a&gt; and creating a new feature branch:&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 &amp;lt;project-url&amp;gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &amp;lt;project-dir&amp;gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; git checkout tags/1.0 -b twilio-support
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now make the following changes to &lt;code&gt;app.py&lt;/code&gt; to evolve it from a simple &lt;code&gt;hello-world&lt;/code&gt; app to enable support for Twilio service too.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s include all the import statements:&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;os&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 3rd party imports&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;chalice&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chalice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;twilio.rest&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;twilio.base.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TwilioRestException&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Twilio Config&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ACCOUNT_SID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&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;ACCOUNT_SID&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AUTH_TOKEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&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;AUTH_TOKEN&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;FROM_NUMBER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&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;FROM_NUMBER&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;TO_NUMBER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&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;TO_NUMBER&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, you&amp;rsquo;ll encapsulate the Twilio API and use it to send SMS:&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;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chalice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sms-shooter&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a Twilio client using account_sid and auth token&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tw_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ACCOUNT_SID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTH_TOKEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/service/sms/send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&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;POST&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;send_sms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;request_body&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_body&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tw_client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;from_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FROM_NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;msg&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TO_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;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sid&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&lt;/span&gt;&lt;span class=&quot;p&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;201&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;headers&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;Content-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;application/json&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;body&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;status&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;success&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                      &lt;span class=&quot;s1&quot;&gt;&amp;#39;data&amp;#39;&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                      &lt;span class=&quot;s1&quot;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;SMS successfully sent&amp;#39;&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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&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;n&quot;&gt;headers&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;Content-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;application/json&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;body&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;status&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;failure&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                      &lt;span class=&quot;s1&quot;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Please try again!!!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TwilioRestException&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&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&lt;/span&gt;&lt;span class=&quot;p&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;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;headers&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;Content-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;application/json&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;body&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;status&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;failure&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;s1&quot;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;o&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the above snippet, you simply create a Twilio client object using &lt;code&gt;ACCOUNT_SID&lt;/code&gt; and &lt;code&gt;AUTH_TOKEN&lt;/code&gt; and use it to send messages under the &lt;code&gt;send_sms&lt;/code&gt; view. &lt;code&gt;send_sms&lt;/code&gt; is a bare bones function that uses the Twilio client&amp;rsquo;s API to send the SMS to the specified destination. Before proceeding further, let&amp;rsquo;s give it a try and run it on our local machine.&lt;/p&gt;
&lt;h3 id=&quot;build-and-run-locally_1&quot;&gt;Build and Run Locally&lt;/h3&gt;
&lt;p&gt;Now you can run your app on your machine using the &lt;code&gt;local&lt;/code&gt; utility and verify that everything is working fine:&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;(env)$&lt;/span&gt; chalice &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now make a curl POST request to &lt;code&gt;http://localhost:8000/service/sms/send&lt;/code&gt; with a specific payload and test the app locally:&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 -H &lt;span class=&quot;s2&quot;&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt; -X POST -d &lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;msg&amp;quot;: &amp;quot;hey mate!!!&amp;quot;}&amp;#39;&lt;/span&gt; http://localhost:8000/service/sms/send
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above request responds as follows:&lt;/p&gt;
&lt;div class=&quot;highlight json&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;nt&quot;&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;success&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;SM60f11033de4f4e39b1c193025bcd5cd8&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;SMS successfully sent&amp;quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The response indicates that the message was successfully sent. Now, let&amp;rsquo;s move on to deploying the app on AWS Lambda.&lt;/p&gt;
&lt;h3 id=&quot;deploy-on-aws-lambda_1&quot;&gt;Deploy on AWS Lambda&lt;/h3&gt;
&lt;p&gt;As suggested in the previous deployment section, you just need to issue 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;(env)$&lt;/span&gt; chalice deploy
&lt;span class=&quot;go&quot;&gt;Creating deployment package.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Updating policy for IAM role: sms-shooter-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Creating lambda function: sms-shooter-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Creating Rest API&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Resources deployed:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - Lambda ARN: arn:aws:lambda:ap-south-1:679337104153:function:sms-shooter-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - Rest API URL: https://qtvndnjdyc.execute-api.ap-south-1.amazonaws.com/api/&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;: The above command succeeds, and you have your API URL in the output as expected. Now on testing the URL, the API throws an error message. &lt;strong&gt;What went wrong?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As per AWS &lt;a href=&quot;https://www.dropbox.com/s/ectzx2std57toaf/Screenshot%202018-11-08%20at%208.35.18%20PM.png?dl=0&quot;&gt;Lambda logs&lt;/a&gt;, &lt;code&gt;twilio&lt;/code&gt; package is not found or installed, so you need to tell the Lambda service to install the dependencies. To do so, you need to add &lt;code&gt;twilio&lt;/code&gt; as a dependency to &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight pyreq&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;twilio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;6.18&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;Other packages such as Chalice and its dependencies should not be included in &lt;code&gt;requirements.txt&lt;/code&gt;, as they are not a part of Python&amp;rsquo;s WSGI runtime. Instead, we should maintain a &lt;code&gt;requirements-dev.txt&lt;/code&gt;, which is applicable to only the development environment and contains all Chalice-related dependencies. To learn more, check out &lt;a href=&quot;https://github.com/aws/chalice/issues/803&quot;&gt;this GitHub issue&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Once all the package dependencies are sorted, you need to make sure all the environment variables are also shipped along and set correctly during the Lambda runtime. To do so, you have to add all the environment variables in &lt;code&gt;.chalice/config.json&lt;/code&gt; in the following manner:&lt;/p&gt;
&lt;div class=&quot;highlight json&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;nt&quot;&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;quot;app_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;sms-shooter&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;quot;stages&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;nt&quot;&gt;&amp;quot;dev&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;nt&quot;&gt;&amp;quot;api_gateway_stage&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;api&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;quot;environment_variables&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;nt&quot;&gt;&amp;quot;ACCOUNT_SID&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;lt;your-account-sid&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;quot;AUTH_TOKEN&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;lt;your-auth-token&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;quot;FROM_NUMBER&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;lt;source-number&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;quot;TO_NUMBER&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;lt;destination-number&amp;gt;&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;Now we&amp;rsquo;re good to deploy:&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;Creating deployment package.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Updating policy for IAM role: sms-shooter-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Updating lambda function: sms-shooter-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Updating rest API&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Resources deployed:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - Lambda ARN: arn:aws:lambda:ap-south-1:679337104153:function:sms-shooter-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - Rest API URL: https://fqcdyzvytc.execute-api.ap-south-1.amazonaws.com/api/&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Do a sanity check by making a curl request to the generated API endpoint:&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 -H &lt;span class=&quot;s2&quot;&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt; -X POST -d &lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;msg&amp;quot;: &amp;quot;hey mate!!!&amp;quot;}&amp;#39;&lt;/span&gt; https://fqcdyzvytc.execute-api.ap-south-1.amazonaws.com/api/service/sms/send
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above request responds as expected:&lt;/p&gt;
&lt;div class=&quot;highlight json&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;nt&quot;&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;success&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;SM60f11033de4f4e39b1c193025bcd5cd8&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;SMS successfully sent&amp;quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, you have a completely serverless SMS sending service up and running. With the front end of this service being a REST API, it can be used in other applications as a plug-and-play feature that is scalable, secure, and reliable.&lt;/p&gt;
&lt;h2 id=&quot;refactoring&quot;&gt;Refactoring&lt;/h2&gt;
&lt;p&gt;Finally, we will refactor our SMS app to not contain all the business logic in &lt;code&gt;app.py&lt;/code&gt; completely. Instead, we will follow the Chalice prescribed best practices and abstract the business logic under the &lt;code&gt;chalicelib/&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s begin by creating a new branch:&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 checkout tags/2.0 -b sms-app-refactor
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, create a new directory in the root directory of the project named &lt;code&gt;chalicelib/&lt;/code&gt; and create a new file named  &lt;code&gt;sms.py&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;(env)$&lt;/span&gt; mkdir chalicelib
&lt;span class=&quot;gp&quot;&gt;(env)$&lt;/span&gt; touch chalicelib/sms.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Update the above created &lt;code&gt;chalicelib/sms.py&lt;/code&gt; with the SMS sending logic by abstracting things from &lt;code&gt;app.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;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;twilio.rest&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Twilio Config&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ACCOUNT_SID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&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;ACCOUNT_SID&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AUTH_TOKEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&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;AUTH_TOKEN&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;FROM_NUMBER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&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;FROM_NUMBER&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;TO_NUMBER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&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;TO_NUMBER&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a twilio client using account_sid and auth token&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tw_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ACCOUNT_SID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTH_TOKEN&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;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload_params&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot; send sms to the specified number &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tw_client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FROM_NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload_params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;msg&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TO_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;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sid&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;msg&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above snippet only accepts the input params and responds as required. Now to make this work, we need to make changes to &lt;code&gt;app.py&lt;/code&gt; as well:&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;# Core imports&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;chalice&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chalice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;twilio.base.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TwilioRestException&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# App level imports&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;chalicelib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sms&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;Chalice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sms-shooter&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;nd&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/&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;index&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;world&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;nd&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/service/sms/send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&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;POST&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;send_sms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;request_body&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_body&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_body&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;resp&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&lt;/span&gt;&lt;span class=&quot;p&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;201&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;headers&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;Content-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;application/json&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;body&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;status&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;success&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                      &lt;span class=&quot;s1&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                      &lt;span class=&quot;s1&quot;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;SMS successfully sent&amp;#39;&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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&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;n&quot;&gt;headers&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;Content-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;application/json&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;body&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;status&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;failure&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                      &lt;span class=&quot;s1&quot;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Please try again!!!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TwilioRestException&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&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&lt;/span&gt;&lt;span class=&quot;p&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;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;headers&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;Content-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;application/json&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;body&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;status&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;failure&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;s1&quot;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;o&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the above snippet, all the SMS sending logic is invoked from the &lt;code&gt;chalicelib.sms&lt;/code&gt; module, making the view layer a lot cleaner in terms of readability. This abstraction lets you add much more complex business logic and customize the functionality as required.&lt;/p&gt;
&lt;h2 id=&quot;sanity-check&quot;&gt;Sanity Check&lt;/h2&gt;
&lt;p&gt;After refactoring our code, let&amp;rsquo;s ensure it is running as expected.&lt;/p&gt;
&lt;h3 id=&quot;build-and-run-locally_2&quot;&gt;Build and Run Locally&lt;/h3&gt;
&lt;p&gt;Run the app once again using the &lt;code&gt;local&lt;/code&gt; utility:&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;(env)$&lt;/span&gt; chalice &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Make a curl request and verify. Once that&amp;rsquo;s done, move on to deployment.&lt;/p&gt;
&lt;h3 id=&quot;deploy-on-aws-lambda_2&quot;&gt;Deploy on AWS Lambda&lt;/h3&gt;
&lt;p&gt;Once you are sure everything is working as expected, you can now finally deploy your app:&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;(env)$&lt;/span&gt; chalice deploy
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As usual, the command executes successfully and you can verify the endpoint.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You now know how to do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build a serverless application using AWS Chalice in accordance with best practices&lt;/li&gt;
&lt;li&gt;Deploy your working app on the Lambda runtime environment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lambda services under the hood are analogous to pure functions, which have a certain behavior on a set of input/output. Developing precise Lambda services allows for better testing, readability, and atomicity. Since Chalice is a minimalist framework, you can just focus on the business logic, and the rest is taken care of, from deployment to IAM policy generation. This is all with just a single command deployment!&lt;/p&gt;
&lt;p&gt;Moreover, Lambda services are mostly focused on heavy CPU bound processing and scale in a self-governed manner, as per the number of requests in a unit of time. Using serverless architecture allows your codebase to be more like SOA (Service Oriented Architecture). Using &lt;a href=&quot;https://realpython.com/python-boto3-aws-s3/&quot;&gt;AWS&amp;rsquo;s&lt;/a&gt; other products in their ecosystem that plug in well with Lambda functions is even more powerful.&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 Emily Morehouse</title>
      <id>https://realpython.com/interview-emily-morehouse/</id>
      <link href="https://realpython.com/interview-emily-morehouse/"/>
      <updated>2018-11-28T14:00:00+00:00</updated>
      <summary>Emily Morehouse is one of the newest additions to the CPython core developer team, and the founder and director of engineering of Cuttlesoft. In this interview, we talk about the recent CPython core developer sprint and the fact that she completed three majors in college at the same time!</summary>
      <content type="html">
        &lt;p&gt;I&amp;rsquo;m very pleased to be joined this week by Emily Morehouse. &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/emilyemorehouse&quot;&gt;Emily&lt;/a&gt; is one of the newest additions to the CPython core developer team, and the founder and director of engineering of Cuttlesoft. Emily and I talk about the recent CPython core developer sprint and the fact that she completed three majors in college at the same time! We&amp;rsquo;ll also get into her passion for compilers and abstract syntax trees. &lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Let’s start with the obvious: how did you get into programming, and when did you start using Python?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3 rounded-circle&quot; src=&quot;https://files.realpython.com/media/emily-square.ee2f3ad095c0.jpg&quot; width=&quot;798&quot; height=&quot;799&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/emily-square.ee2f3ad095c0.jpg&amp;amp;w=199&amp;amp;sig=da026d01ecdcb8d41a7c2515f09bc940d0344e4d 199w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/emily-square.ee2f3ad095c0.jpg&amp;amp;w=399&amp;amp;sig=8fa3f33d5ce419c8633e8d4c4fc7e9f9a9285e0d 399w, https://files.realpython.com/media/emily-square.ee2f3ad095c0.jpg 798w&quot; sizes=&quot;75vw&quot; alt=&quot;Emily Morehouse&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Emily:&lt;/strong&gt; My path to programming started with falling in love with Enigma machines. Really, I stumbled into programming during college. I had recently switched one of my majors from Biochemistry to Criminology, and the department had just launched a Computer Criminology program.&lt;/p&gt;
&lt;p&gt;I was encouraged to try out the Intro To Programming course to see how I liked it. One of our final projects was building an Enigma machine simulator (in C++, mind you), and I was hooked. I decided to add a third major to take on a full Computer Science degree (more on that later!).&lt;/p&gt;
&lt;p&gt;Since the CS program was highly theoretical and focused on languages like C and C++, I started to find ways outside of coursework to learn different things. I picked up Python working on web scrapers on the weekends and was eventually hired as a researcher where we used Python to scrape and analyze public data from various sites.&lt;/p&gt;
&lt;p&gt;For me, programming spans this wide range of challenging logic and technical problems to more abstract concepts of how humans think and interact with machines and how technology can enhance our daily lives. It fills a gap between academics and art that I didn&amp;rsquo;t know I needed to, or could, fill.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;As you&amp;rsquo;ve already alluded to, you attended Florida State University, where you completed your CS degree. And a degree in Criminology. And another one in Theater… Did you ever sleep? One degree is hard, but three at once? I’m really curious to know your secrets and any time management hacks for studying and learning to code when you have so much else going on.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Emily:&lt;/strong&gt; I definitely did not sleep much. On top of all of my schoolwork, I worked a nearly full-time job and even worked as an overnight manager at our local coffee shop while still participating in theater rehearsals and performances.&lt;/p&gt;
&lt;p&gt;I was able to get a research position in the CS department to eliminate some of that pressure. I was lucky to have started college with a lot of credits and tested out of a few courses, so I was technically already a year ahead, which gave me more freedom to try out courses like Programming.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s all very much how I was raised. From a very young age, I knew that my day started around 7 a.m. I went straight from school to rehearsals and dance classes, then had to do homework until I fell asleep. I had to learn how to retain information and figure things out quickly&amp;mdash;and I had to stay organized, so I made a lot of lists.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve asked my parents how I came to be this way, and they just shrug! I&amp;rsquo;ve always felt very in control of how I spend my time to ensure it&amp;rsquo;s what I want to be doing, and I think that&amp;rsquo;s important when staying so busy. You have to want to be doing everything, or else things will fall by the wayside.&lt;/p&gt;
&lt;p&gt;I definitely suggest finding a manner of keeping to-do lists and prioritizing your time. I use an app called &lt;a href=&quot;https://bear.app/&quot;&gt;Bear&lt;/a&gt; (like a simplified Evernote, but with programmer-friendly themes and markdown support) along with a lot of task prioritization.&lt;/p&gt;
&lt;p&gt;I also figured out that I learn things quickly by writing them down multiple times. I used this method to memorize lines for shows. I&amp;rsquo;d white-out my lines then go back and physically write them down from memory on a separate piece of paper, rinse, and repeat. I got to the point where if I wrote something down 1 to 2 times, it&amp;rsquo;d stick.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;You are the co-founder and director of engineering at Cuttlesoft. It looks as if you started the company before finishing college. What was your motivation for starting your own business instead of applying for junior software developer jobs straight out of college?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Emily:&lt;/strong&gt; &lt;a href=&quot;https://www.cuttlesoft.com/&quot;&gt;Cuttlesoft&lt;/a&gt; was a matter of circumstance. I never imagined I&amp;rsquo;d run my own company, especially not alongside my now-husband Frank. I was in a weird timing limbo where I&amp;rsquo;d finished my undergraduate degrees earlier than expected which meant that I missed all of the grad school deadlines.&lt;/p&gt;
&lt;p&gt;FSU agreed to let me jump in and start my Masters there, and my intention was to stay for a year then transfer elsewhere where I could continue working with parsers, compilers, and formal verification. I was also getting recruited by huge tech companies, and I was a bit enamored with the idea of living in San Francisco or Boston. (I&amp;rsquo;d only ever lived in Florida at the time.)&lt;/p&gt;
&lt;p&gt;But then Frank and I had found our way into this budding entrepreneurship ecosystem in Tallahassee. We met a few people who became great mentors, and before we could even get our Is dotted, we had our first couple of clients. I thought, &amp;ldquo;Why do I want to leave all of these people who are invested in my future and success to go be one of the thousands somewhere else?”&lt;/p&gt;
&lt;p&gt;I figured that I should take the chance on starting something of my own and continuing on this rapid growth path. I knew I&amp;rsquo;d learn a lot more in a shorter amount of time than I would almost anywhere else. So I dropped out of graduate school after my first semester and put all of my time into Cuttlesoft.&lt;/p&gt;
&lt;p&gt;Looking back, I can&amp;rsquo;t imagine a different path for me. Soon after I turned down those job offers, Susan Fowler&amp;rsquo;s story came to light. I couldn&amp;rsquo;t help but think, “That could have been me.&amp;rdquo; I truly believe that a company&amp;rsquo;s culture is top-down, and I&amp;rsquo;m grateful to get to contribute to a company where I can make a huge impact in a positive manner.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;This year, you got to fulfill a dream and speak at PyCon, with your talk titled The AST and Me. I’ll admit, some of it went over my head, but I’m still learning. I got the impression that language internals fascinate you. What advice would you give to someone who is at the start of their coding journey and wants to know more about how the sausage is made? What resources would you recommend?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Emily:&lt;/strong&gt; Yes! I was the weird kid in university who loved the classes that most everyone else hated (Programming Languages, Compilers, Theory of Computation…). I would spend hours drawing out &lt;a href=&quot;http://www.csd.uwo.ca/~moreno/CS447/Lectures/Lexical.html/node3.html&quot;&gt;non-deterministic finite automata&lt;/a&gt; and &lt;a href=&quot;https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-01sc-introduction-to-electrical-engineering-and-computer-science-i-spring-2011/unit-1-software-engineering/state-machines/MIT6_01SCS11_chap04.pdf&quot;&gt;state machines&lt;/a&gt; for my course notes.&lt;/p&gt;
&lt;p&gt;I’m a huge fan of &lt;a href=&quot;https://pragprog.com/book/btlang/seven-languages-in-seven-weeks&quot;&gt;Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages by Bruce A. Tate&lt;/a&gt; to get a feel for different programming language paradigms. The Dragon Book (&lt;a href=&quot;https://realpython.com/asins/9332518661/&quot;&gt;Compilers: Principles, Techniques, and Tools&lt;/a&gt;) is a classic and is the backbone of so much we still use today. (Python&amp;rsquo;s compiler is based on this.) &lt;a href=&quot;http://pgbovine.net/cpython-internals.htm&quot;&gt;Philip Guo’s video series&lt;/a&gt; on CPython internals is also awesome and helped me in my journey diving into how Python works under the hood.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Huge congratulations are in order, as you have just been promoted to a CPython core developer! You must be so thrilled. How was the initiation at the recent CPython sprints? Anything exciting to share or any stories to tell? Don’t worry, we can keep a secret…&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Emily:&lt;/strong&gt; Thank you! The CPython Sprint was a lot of fun. It’s rare that we get so many core developers in the same room working together. We&amp;rsquo;re all incredibly grateful for the PSF and this year&amp;rsquo;s sprint sponsor, Microsoft, for supporting CPython.&lt;/p&gt;
&lt;p&gt;I was able to attend sprints and Language Summits at PyCons past and had the chance to get to know a lot of the group previously, so this sprint felt surprisingly normal, but it was super cool to see and work with everyone in person.&lt;/p&gt;
&lt;p&gt;I spent most of the sprint implementing &lt;a href=&quot;https://www.python.org/dev/peps/pep-0572/&quot;&gt;PEP 572&lt;/a&gt;, the (in)famous assignment expressions PEP, with Guido&amp;rsquo;s guidance. No matter which side of the fence you fell on with assignment expressions (or I as lovingly now call it, the &lt;a href=&quot;https://twitter.com/squeaky_pl/status/1020284300359553024&quot;&gt;walrus operator&lt;/a&gt;), it&amp;rsquo;s been incredibly cool to add new syntax to the language and deep dive into the internals to get variable scoping to work as intended. It will be in the alpha versions of 3.8 early next year, so keep an eye out!&lt;/p&gt;
&lt;p&gt;One of my favorite parts of the sprint was getting to know more about the history of CPython. Since the beginning of my path in core development, I&amp;rsquo;ve found that it’s really interesting to hear the stories of how others became core developers, so I pose that question to everyone I can.&lt;/p&gt;
&lt;p&gt;Understanding everyone&amp;rsquo;s journey and motivations for devoting so much of their time and energy to a project (especially those who have been involved since the very early days) is an important step to understanding how to continue growing the group and increasing diversity.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Now for my last question: what other hobbies and interests do you have, aside from Python? Any you’d like to share and/or plug?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Emily:&lt;/strong&gt; I try to take advantage of everything Colorado has to offer in my spare time&amp;mdash;coming from Florida, I&amp;rsquo;m still totally enamored with the Rocky Mountains and love hiking. Denver is also a great foodie city. &lt;/p&gt;
&lt;p&gt;When I make the time for it, I also really enjoy yoga, reading, listening to podcasts, playing video games (though I&amp;rsquo;m still slowly working through the most recent God of War), and trying to keep my houseplants alive. I also enjoy spending time with my husband and our dog&amp;mdash;they&amp;rsquo;re my world.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Thank you, Emily, for joining me this week. You can follow Emily&amp;rsquo;s work on &lt;a href=&quot;https://twitter.com/emilyemorehouse&quot;&gt;Twitter&lt;/a&gt; or &lt;a href=&quot;https://github.com/emilyemorehouse&quot;&gt;Github&lt;/a&gt;. Find out more about her company, Cuttlesoft, &lt;a href=&quot;https://www.cuttlesoft.com/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If there&amp;rsquo;s someone you&amp;rsquo;d like me to interview in the future, reach out to me in the comments below, or &lt;a href=&quot;https://twitter.com/EndlessTrax&quot;&gt;send me a message on Twitter&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>Continuous Integration With Python: An Introduction</title>
      <id>https://realpython.com/python-continuous-integration/</id>
      <link href="https://realpython.com/python-continuous-integration/"/>
      <updated>2018-11-26T14:00:00+00:00</updated>
      <summary>In this Python tutorial, you&#39;ll learn the core concepts behind Continuous Integration (CI) and why they are essential for modern software engineering teams. Find out how to how set up Continuous Integration for your Python project to automatically create environments, install dependencies, and run tests.</summary>
      <content type="html">
        &lt;p&gt;When writing code on your own, the only priority is making it work. However, working in a team of professional software developers brings a plethora of challenges. One of those challenges is coordinating many people working on the same code.&lt;/p&gt;
&lt;p&gt;How do professional teams make dozens of changes per day while making sure everyone is coordinated and nothing is broken? Enter continuous integration!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial you&amp;rsquo;ll:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learn the core concepts behind continuous integration&lt;/li&gt;
&lt;li&gt;Understand the benefits of continuous integration&lt;/li&gt;
&lt;li&gt;Set up a basic continuous integration system&lt;/li&gt;
&lt;li&gt;Create a simple Python example and connect it to the continuous integration system&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;what-is-continuous-integration&quot;&gt;What Is Continuous Integration?&lt;/h2&gt;
&lt;p&gt;Continuous integration (CI) is the practice of frequently building and testing each change done to your code automatically and as early as possible. Prolific developer and author Martin Fowler defines CI as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible.&amp;rdquo; (&lt;a href=&quot;https://martinfowler.com/articles/continuousIntegration.html&quot;&gt;Source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;rsquo;s unpack this.&lt;/p&gt;
&lt;p&gt;Programming is iterative. The source code lives in a repository that is shared by all members of the team. If you want to work on that product, you must obtain a copy. You will make changes, test them, and integrate them back into the main repo. Rinse and repeat.&lt;/p&gt;
&lt;p&gt;Not so long ago, these integrations were big and weeks (or months) apart, causing headaches, wasting time, and losing money. Armed with experience, developers started making minor changes and integrating them more frequently. This reduces the chances of introducing conflicts that you need to resolve later.&lt;/p&gt;
&lt;p&gt;After every integration, you need to build the source code. Building means transforming your high-level code into a format your computer knows how to run. Finally, the result is systematically tested to ensure your changes did not introduce errors.&lt;/p&gt;
&lt;h2 id=&quot;why-should-i-care&quot;&gt;Why Should I Care?&lt;/h2&gt;
&lt;p&gt;On a personal level, continuous integration is really about how you and your colleagues spend your time.&lt;/p&gt;
&lt;p&gt;Using CI, you&amp;rsquo;ll spend less time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Worrying about introducing a bug every time you make changes&lt;/li&gt;
&lt;li&gt;Fixing the mess someone else made so you can integrate your code&lt;/li&gt;
&lt;li&gt;Making sure the code works on every machine, operating system, and browser&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Conversely, you&amp;rsquo;ll spend more time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Solving interesting problems&lt;/li&gt;
&lt;li&gt;Writing awesome code with your team&lt;/li&gt;
&lt;li&gt;Co-creating amazing products that provide value to users&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How does that sound?&lt;/p&gt;
&lt;p&gt;On a team level, it allows for a better engineering culture, where you deliver value early and often. Collaboration is encouraged, and bugs are caught much sooner. Continuous integration will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make you and your team faster&lt;/li&gt;
&lt;li&gt;Give you confidence that you&amp;rsquo;re building stable software with fewer bugs&lt;/li&gt;
&lt;li&gt;Ensure that your product works on other machines, not just your laptop&lt;/li&gt;
&lt;li&gt;Eliminate a lot of tedious overhead and let you focus on what matters&lt;/li&gt;
&lt;li&gt;Reduce the time spent resolving conflicts (when different people modify the same code)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;core-concepts&quot;&gt;Core Concepts&lt;/h2&gt;
&lt;p&gt;There are several key ideas and practices that you need to understand to work effectively with continuous integration. Also, there might be some words and phrases you aren&amp;rsquo;t familiar with but are used often when you&amp;rsquo;re talking about CI. This chapter will introduce you to these concepts and the jargon that comes with them.&lt;/p&gt;
&lt;h3 id=&quot;single-source-repository&quot;&gt;Single Source Repository&lt;/h3&gt;
&lt;p&gt;If you are collaborating with others on a single code base, it&amp;rsquo;s typical to have a shared repository of source code. Every developer working on the project creates a local copy and makes changes. Once they are satisfied with the changes, they merge them back into the central repository.&lt;/p&gt;
&lt;p&gt;It has become a standard to use version control systems (VCS) like Git to handle this workflow for you. Teams typically use an external service to host their source code and handle all the moving parts. The most popular are GitHub, BitBucket, and GitLab.&lt;/p&gt;
&lt;p&gt;Git allows you to create multiple &lt;strong&gt;branches&lt;/strong&gt; of a repository. Each branch is an independent copy of the source code and can be modified without affecting other branches. This is an essential feature, and most teams have a mainline branch (often called a master branch) that represents the current state of the project.&lt;/p&gt;
&lt;p&gt;If you want to add or modify code, you should create a copy of the main branch and work in your new, development branch. Once you are done, merge those changes back into the master branch.&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/CI1-Branching_2.fe69572f8bcb.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/CI1-Branching_2.fe69572f8bcb.png&quot; width=&quot;522&quot; height=&quot;141&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CI1-Branching_2.fe69572f8bcb.png&amp;amp;w=130&amp;amp;sig=0ebbe1c7b6a5dac2167313a457e68bca46277691 130w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CI1-Branching_2.fe69572f8bcb.png&amp;amp;w=261&amp;amp;sig=f5d7a1abe8c4e73e1f3696cddb3b80b2beb8391b 261w, https://files.realpython.com/media/CI1-Branching_2.fe69572f8bcb.png 522w&quot; sizes=&quot;75vw&quot; alt=&quot;Git Branching&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Git branching&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Version control holds more than just code. Documentation and test scripts are usually stored along with the source code. Some programs look for external files used to configure their parameters and initial settings. Other applications need a database schema. All these files should go into your repository.&lt;/p&gt;
&lt;p&gt;If you have never used Git or need a refresher, check out our &lt;a href=&quot;https://realpython.com/python-git-github-intro/&quot;&gt;Introduction to Git and GitHub for Python Developers&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;automating-the-build&quot;&gt;Automating the Build&lt;/h3&gt;
&lt;p&gt;As previously mentioned, building your code means taking the raw source code, and everything necessary for its execution, and translating it into a format that computers can run directly. Python is an &lt;a href=&quot;https://en.wikipedia.org/wiki/Interpreted_language&quot;&gt;interpreted language&lt;/a&gt;, so its &amp;ldquo;build&amp;rdquo; mainly revolves around test execution rather than compilation.&lt;/p&gt;
&lt;p&gt;Running those steps manually after every small change is tedious and takes valuable time and attention from the actual problem-solving you&amp;rsquo;re trying to do. A big part of continuous integration is automating that process and moving it out of sight (and out of mind).&lt;/p&gt;
&lt;p&gt;What does that mean for Python? Think about a more complicated piece of code you have written. If you used a library, package, or framework that doesn&amp;rsquo;t come with the Python standard library (think anything you needed to install with &lt;code&gt;pip&lt;/code&gt; or &lt;code&gt;conda&lt;/code&gt;), Python needs to know about that, so the program knows where to look when it finds commands that it doesn&amp;rsquo;t recognize.&lt;/p&gt;
&lt;p&gt;You store a list of those packages in &lt;code&gt;requirements.txt&lt;/code&gt; or a Pipfile. These are the dependencies of your code and are necessary for a successful build.&lt;/p&gt;
&lt;p&gt;You will often hear the phrase &amp;ldquo;breaking the build.&amp;rdquo; When you break the build, it means you introduced a change that rendered the final product unusable. Don&amp;rsquo;t worry. It happens to everyone, even battle-hardened senior developers. You want to avoid this primarily because it will block everyone else from working.&lt;/p&gt;
&lt;p&gt;The whole point of CI is to have everyone working on a known stable base. If they clone a repository that is breaking the build, they will work with a broken version of the code and won&amp;rsquo;t be able to introduce or test their changes. When you break the build, the top priority is fixing it so everyone can resume work.&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/CI1-BreakingChange.45a0e6b1fa07.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/CI1-BreakingChange.45a0e6b1fa07.png&quot; width=&quot;442&quot; height=&quot;141&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CI1-BreakingChange.45a0e6b1fa07.png&amp;amp;w=110&amp;amp;sig=b057b006db2f401e87b80d9e9d18db5527479993 110w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CI1-BreakingChange.45a0e6b1fa07.png&amp;amp;w=221&amp;amp;sig=e1d5aa3e4e6eef03d10710fd93eedab70ef3c2bd 221w, https://files.realpython.com/media/CI1-BreakingChange.45a0e6b1fa07.png 442w&quot; sizes=&quot;75vw&quot; alt=&quot;Pushing Breaking Changes to Master&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Introducing a breaking change to the master branch&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;When the build is automated, you are encouraged to commit frequently, usually multiple times per day. It allows people to quickly find out about changes and notice if there&amp;rsquo;s a conflict between two developers. If there are numerous small changes instead of a few massive updates, it&amp;rsquo;s much easier to locate where the error originated. It will also encourage you to break your work down into smaller chunks, which is easier to track and test.&lt;/p&gt;
&lt;h3 id=&quot;automated-testing&quot;&gt;Automated Testing&lt;/h3&gt;
&lt;p&gt;Since everyone is committing changes multiple times per day, it&amp;rsquo;s important to know that your change didn&amp;rsquo;t break anything else in the code or introduce bugs. In many companies, testing is now a responsibility of every developer. If you write code, you should write tests. At a bare minimum, you should cover every new function with a unit test.&lt;/p&gt;
&lt;p&gt;Running tests automatically, with every change committed, is a great way to catch bugs. A failing test automatically causes the build to fail. It will draw your attention to the problems revealed by testing, and the failed build will make you fix the bug you introduced. Tests don&amp;rsquo;t guarantee that your code is free of bugs, but it does guard against a lot of careless changes.&lt;/p&gt;
&lt;p&gt;Automating test execution gives you some peace of mind because you know the server will test your code every time you commit, even if you forgot to do it locally.&lt;/p&gt;
&lt;h3 id=&quot;using-an-external-continuous-integration-service&quot;&gt;Using an External Continuous Integration Service&lt;/h3&gt;
&lt;p&gt;If something works on your computer, will it work on every computer? Probably not. It&amp;rsquo;s a cliché excuse and a sort of inside joke among developers to say, &amp;ldquo;Well, it worked on my machine!&amp;rdquo; Making the code work locally is not the end of your responsibility.&lt;/p&gt;
&lt;p&gt;To tackle this problem, most companies use an external service to handle integration, much like using GitHub for hosting your source code repository. External services have servers where they build code and run tests. They act as monitors for your repository and stop anyone from merging to the master branch if their changes break the build.&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/CI-Testing_1.dd4a5d09bedd.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/CI-Testing_1.dd4a5d09bedd.png&quot; width=&quot;342&quot; height=&quot;240&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CI-Testing_1.dd4a5d09bedd.png&amp;amp;w=85&amp;amp;sig=34125e392012bc543a685eea6beaf3fc353944fd 85w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CI-Testing_1.dd4a5d09bedd.png&amp;amp;w=171&amp;amp;sig=98f1edc5d0131db279fadd37fe332a1111b913d7 171w, https://files.realpython.com/media/CI-Testing_1.dd4a5d09bedd.png 342w&quot; sizes=&quot;75vw&quot; alt=&quot;Automated Testing&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Merging changes triggers the CI server&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;There are many such services out there, with various features and pricing. Most have a free tier so that you can experiment with one of your repositories. You will use a service called CircleCI in an example later in the tutorial.&lt;/p&gt;
&lt;h3 id=&quot;testing-in-a-staging-environment&quot;&gt;Testing in a Staging Environment&lt;/h3&gt;
&lt;p&gt;A production environment is where your software will ultimately run. Even after successfully building and testing your application, you can&amp;rsquo;t be sure that your code will work on the target computer. That&amp;rsquo;s why teams deploy the final product in an environment that mimics the production environment. Once you are sure everything works, the application is deployed in the production environment.&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 step is more relevant to application code than library code. Any Python libraries you write still need to be tested on a build server, to ensure they work in environments different from your local computer.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You will hear people talking about this &lt;strong&gt;clone&lt;/strong&gt; of the production environment using terms like development environment, staging environment, or testing environment. It&amp;rsquo;s common to use abbreviations like DEV for the development environment and PROD for the production environment.&lt;/p&gt;
&lt;p&gt;The development environment should replicate production conditions as closely as possible. This setup is often called &lt;strong&gt;DEV/PROD parity&lt;/strong&gt;. Keep the environment on your local computer as similar as possible to the DEV and PROD environments to minimize anomalies when deploying applications.&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/DEV_PROD_2_2.ae771984fb55.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/DEV_PROD_2_2.ae771984fb55.png&quot; width=&quot;721&quot; height=&quot;430&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/DEV_PROD_2_2.ae771984fb55.png&amp;amp;w=180&amp;amp;sig=6d55dcd21397897f2729ae560f3ca72ab7641dca 180w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/DEV_PROD_2_2.ae771984fb55.png&amp;amp;w=360&amp;amp;sig=edd203903b700a9e447fe3b16f5e64b1c2b4a152 360w, https://files.realpython.com/media/DEV_PROD_2_2.ae771984fb55.png 721w&quot; sizes=&quot;75vw&quot; alt=&quot;DEV/PROD Parity&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Test in a clone of the production environment&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;We mention this to introduce you to the vocabulary, but continuously deploying software to DEV and PROD is a whole other topic. The process is called, unsurprisingly, continuous deployment (CD). You can find more resources about it in the &lt;a href=&quot;#next-steps&quot;&gt;Next Steps&lt;/a&gt; section of this article.&lt;/p&gt;
&lt;h2 id=&quot;your-turn&quot;&gt;Your Turn!&lt;/h2&gt;
&lt;p&gt;The best way to learn is by doing. You now understand all the essential practices of continuous integration, so it&amp;rsquo;s time to get your hands dirty and create the whole chain of steps necessary to use CI. This chain is often called a CI &lt;strong&gt;pipeline&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is a hands-on tutorial, so fire up your editor and get ready to work through these steps as you read!&lt;/p&gt;
&lt;p&gt;We assume that you know the basics of Python and Git. We will use &lt;a href=&quot;https://github.com/&quot;&gt;Github&lt;/a&gt; as our hosting service and &lt;a href=&quot;https://circleci.com/&quot;&gt;CircleCI&lt;/a&gt; as our external continuous integration service. If you don&amp;rsquo;t have accounts with these services, go ahead and register. Both of these have free tiers!&lt;/p&gt;
&lt;h3 id=&quot;problem-definition&quot;&gt;Problem Definition&lt;/h3&gt;
&lt;p&gt;Remember, your focus here is adding a new tool to your utility belt, continuous integration. For this example, the Python code itself will be straightforward. You want to spend the bulk of your time internalizing the steps of building a pipeline, instead of writing complicated code.&lt;/p&gt;
&lt;p&gt;Imagine your team is working on a simple calculator app. Your task is to write a library of basic mathematical functions: addition, subtraction, multiplication, and division. You don&amp;rsquo;t care about the actual application, because that&amp;rsquo;s what your peers will be developing, using functions from your library.&lt;/p&gt;
&lt;h3 id=&quot;create-a-repo&quot;&gt;Create a Repo&lt;/h3&gt;
&lt;p&gt;Log in to your GitHub account, create a new repository and call it &lt;em&gt;CalculatorLibrary&lt;/em&gt;. Add a README and .gitignore, then clone the repository to your local machine. If you need more help with this process, have a look at GitHub&amp;rsquo;s &lt;a href=&quot;https://help.github.com/articles/creating-a-new-repository/&quot;&gt;walkthrough&lt;/a&gt; on creating a new repository.&lt;/p&gt;
&lt;h3 id=&quot;set-up-a-working-environment&quot;&gt;Set Up a Working Environment&lt;/h3&gt;
&lt;p&gt;For others (and the CI server) to replicate your working conditions, you need to set up an environment. Create a virtual environment somewhere outside your repo and activate it:&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;c1&quot;&gt;# Create virtual environment&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3 -m venv calculator

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Activate virtual environment (Mac and Linux)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; . calculator/bin/activate
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The previous commands work on macOS and Linux. If you are a Windows user, check the Platforms table in the &lt;a href=&quot;https://docs.python.org/3.7/library/venv.html#creating-virtual-environments&quot;&gt;official documentation&lt;/a&gt;. This will create a directory that contains a Python installation and tell the interpreter to use it. Now we can install packages knowing that it will not influence your system&amp;rsquo;s default Python installation.&lt;/p&gt;
&lt;h3 id=&quot;write-a-simple-python-example&quot;&gt;Write a Simple Python Example&lt;/h3&gt;
&lt;p&gt;Create a new file called &lt;code&gt;calculator.py&lt;/code&gt; in the top-level directory of your repository, and copy 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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;Calculator library containing basic math operations.&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_term&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second_term&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;first_term&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second_term&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;subtract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_term&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second_term&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;first_term&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second_term&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a bare-bones example containing two of the four functions we will be writing. Once we have our CI pipeline up and running, you will add the remaining two functions.&lt;/p&gt;
&lt;p&gt;Go ahead and commit those changes:&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;c1&quot;&gt;# Make sure you are in the correct directory&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; CalculatorLibrary
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; git add calculator.py
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; git commit -m &lt;span class=&quot;s2&quot;&gt;&amp;quot;Add functions for addition and subtraction&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your &lt;em&gt;CalculatorLibrary&lt;/em&gt; folder should have the following files right now:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;CalculatorLibrary/
|
├── .git
├── .gitignore
├── README.md
└── calculator.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Great, you have completed one part of the required functionality. The next step is adding tests to make sure your code works the way it&amp;rsquo;s supposed to.&lt;/p&gt;
&lt;h3 id=&quot;write-unit-tests&quot;&gt;Write Unit Tests&lt;/h3&gt;
&lt;p&gt;You will test your code in two steps.&lt;/p&gt;
&lt;p&gt;The first step involves linting&amp;mdash;running a program, called a linter, to analyze code for potential errors. &lt;a href=&quot;http://flake8.pycqa.org/en/latest/&quot;&gt;&lt;code&gt;flake8&lt;/code&gt;&lt;/a&gt; is commonly used to check if your code conforms to the standard Python coding style. Linting makes sure your code is easy to read for the rest of the Python community.&lt;/p&gt;
&lt;p&gt;The second step is unit testing. A unit test is designed to check a single function, or unit, of code. Python comes with a standard unit testing library, but other libraries exist and are very popular. This example uses &lt;a href=&quot;https://docs.pytest.org/en/latest/&quot;&gt;&lt;code&gt;pytest&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A standard practice that goes hand in hand with testing is calculating code coverage. Code coverage is the percentage of source code that is &amp;ldquo;covered&amp;rdquo; by your tests. &lt;code&gt;pytest&lt;/code&gt; has an extension, &lt;code&gt;pytest-cov&lt;/code&gt;, that helps you understand your code coverage.&lt;/p&gt;
&lt;p&gt;These are external dependencies, and you need to install them:&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 flake8 pytest pytest-cov
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are the only external packages you will use. Make sure to store those dependencies in a &lt;code&gt;requirements.txt&lt;/code&gt; file so others can replicate your 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; pip freeze &amp;gt; requirements.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To run your linter, execute 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; flake8 --statistics
&lt;span class=&quot;go&quot;&gt;./calculator.py:3:1: E302 expected 2 blank lines, found 1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;./calculator.py:6:1: E302 expected 2 blank lines, found 1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2     E302 expected 2 blank lines, found 1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;--statistics&lt;/code&gt; option gives you an overview of how many times a particular error happened. Here we have two PEP 8 violations, because &lt;code&gt;flake8&lt;/code&gt; expects two blank lines before a function definition instead of one. Go ahead and add an empty line before each functions definition. Run &lt;code&gt;flake8&lt;/code&gt; again to check that the error messages no longer appear.&lt;/p&gt;
&lt;p&gt;Now it&amp;rsquo;s time to write the tests. Create a file called &lt;code&gt;test_calculator.py&lt;/code&gt; in the top-level directory of your repository and copy 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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;Unit tests for the calculator library&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;calculator&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalculator&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_addition&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;assert&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calculator&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;mi&quot;&gt;2&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_subtraction&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;assert&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calculator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subtract&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;These tests make sure that our code works as expected. It is far from extensive because you haven&amp;rsquo;t tested for potential misuse of your code, but keep it simple for now.&lt;/p&gt;
&lt;p&gt;The following command runs 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; pytest -v --cov
&lt;span class=&quot;go&quot;&gt;collected 2 items&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;test_calculator.py::TestCalculator::test_addition PASSED [50%]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;test_calculator.py::TestCalculator::test_subtraction PASSED [100%]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;---------- coverage: platform darwin, python 3.6.6-final-0 -----------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Name                                              Stmts   Miss  Cover&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;---------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;calculator.py                                         4      0   100%&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;test_calculator.py                                    6      0   100%&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;/Users/kristijan.ivancic/code/learn/__init__.py       0      0   100%&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;---------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;TOTAL                                                10      0   100%&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;pytest&lt;/code&gt; is excellent at test discovery. Because you have a file with the prefix &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;pytest&lt;/code&gt; knows it will contain unit tests for it to run. The same principles apply to the class and method names inside the file.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-v&lt;/code&gt; flag gives you a nicer output, telling you which tests passed and which failed. In our case, both tests passed. The &lt;code&gt;--cov&lt;/code&gt; flag makes sure &lt;code&gt;pytest-cov&lt;/code&gt; runs and gives you a code coverage report for &lt;code&gt;calculator.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You have completed the preparations. Commit the test file and push all those changes to the master branch:&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 add test_calculator.py
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; git commit -m &lt;span class=&quot;s2&quot;&gt;&amp;quot;Add unit tests for calculator&amp;quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; git push
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At the end of this section, your &lt;em&gt;CalculatorLibrary&lt;/em&gt; folder should have the following files:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;CalculatorLibrary/
|
├── .git
├── .gitignore
├── README.md
├── calculator.py
├── requirements.txt
└── test_calculator.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Excellent, both your functions are tested and work correctly.&lt;/p&gt;
&lt;h3 id=&quot;connect-to-circleci&quot;&gt;Connect to CircleCI&lt;/h3&gt;
&lt;p&gt;At last, you are ready to set up your continuous integration pipeline!&lt;/p&gt;
&lt;p&gt;CircleCI needs to know how to run your build and expects that information to be supplied in a particular format. It requires a &lt;code&gt;.circleci&lt;/code&gt; folder within your repo and a configuration file inside it. A configuration file contains instructions for all the steps that the build server needs to execute. CircleCI expects this file to be called &lt;code&gt;config.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;.yml&lt;/code&gt; file uses a data serialization language, YAML, and it has its own &lt;a href=&quot;http://yaml.org/spec/&quot;&gt;specification&lt;/a&gt;. The goal of YAML is to be human readable and to work well with modern programming languages for common, everyday tasks.&lt;/p&gt;
&lt;p&gt;In a YAML file, there are three basic ways to represent data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mappings (key-value pairs)&lt;/li&gt;
&lt;li&gt;Sequences (lists)&lt;/li&gt;
&lt;li&gt;Scalars (strings or numbers)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is very simple to read:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indentation may be used for structure.&lt;/li&gt;
&lt;li&gt;Colons separate key-value pairs.&lt;/li&gt;
&lt;li&gt;Dashes are used to create lists.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create the &lt;code&gt;.circleci&lt;/code&gt; folder in your repo and a &lt;code&gt;config.yml&lt;/code&gt; file with the following content:&lt;/p&gt;
&lt;div class=&quot;highlight yaml&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Python CircleCI 2.0 configuration file&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;version&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;2&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;docker&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;image&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;circleci/python:3.7&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;working_directory&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;~/repo&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Step 1: obtain repo from GitHub&lt;/span&gt;
      &lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;checkout&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Step 2: create virtual env and install dependencies&lt;/span&gt;
      &lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&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;install dependencies&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;command&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;no&quot;&gt;python3 -m venv venv&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;. venv/bin/activate&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;pip install -r requirements.txt&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Step 3: run linter and tests&lt;/span&gt;
      &lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&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;run tests&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;command&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;no&quot;&gt;. venv/bin/activate&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;flake8 --exclude=venv* --statistics&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;pytest -v --cov=calculator&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some of these words and concepts might be unfamiliar to you. For example, what is Docker, and what are images? Let&amp;rsquo;s go back in time a bit.&lt;/p&gt;
&lt;p&gt;Remember the problem programmers face when something works on their laptop but nowhere else? Before, developers used to create a program that isolates a part of the computer&amp;rsquo;s physical resources (memory, hard drive, and so on) and turns them into a &lt;strong&gt;virtual machine&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A virtual machine pretends to be a whole computer on its own. It would even have its own operating system. On that operating system, you deploy your application or install your library and test it.&lt;/p&gt;
&lt;p&gt;Virtual machines take up a lot of resources, which sparked the invention of containers. The idea is analogous to shipping containers. Before shipping containers were invented, manufacturers had to ship goods in a wide variety of sizes, packaging, and modes (trucks, trains, ships).&lt;/p&gt;
&lt;p&gt;By standardizing the shipping container, these goods could be transferred between different shipping methods without any modification. The same idea applies to software containers.&lt;/p&gt;
&lt;p&gt;Containers are a lightweight unit of code and its runtime dependencies, packaged in a standardized way, so they can quickly be plugged in and run on the Linux OS. You don&amp;rsquo;t need to create a whole virtual operating system, as you would with a virtual machine.&lt;/p&gt;
&lt;p&gt;Containers only replicate parts of the operating system they need in order to work. This reduces their size and gives them a big performance boost.&lt;/p&gt;
&lt;p&gt;Docker is currently the leading container platform, and it&amp;rsquo;s even able to run Linux containers on Windows and macOS. To create a Docker container, you need a Docker image. Images provide blueprints for containers much like classes provide blueprints for objects. You can read more about Docker in their &lt;a href=&quot;https://docs.docker.com/get-started/&quot;&gt;Get Started&lt;/a&gt; guide.&lt;/p&gt;
&lt;p&gt;CircleCI maintains &lt;a href=&quot;https://circleci.com/docs/2.0/circleci-images/&quot;&gt;pre-built Docker images&lt;/a&gt; for several programming languages. In the above configuration file, you have specified a Linux image that has Python already installed. That image will create a container in which everything else happens.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at each line of the configuration file in turn:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;:&lt;/strong&gt; Every &lt;code&gt;config.yml&lt;/code&gt; starts with the CircleCI version number, used to issue warnings about breaking changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;jobs&lt;/code&gt;:&lt;/strong&gt; Jobs represent a single execution of the build and are defined by a collection of steps. If you have only one job, it must be called &lt;code&gt;build&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;build&lt;/code&gt;:&lt;/strong&gt; As mentioned before, &lt;code&gt;build&lt;/code&gt; is the name of your job. You can have multiple jobs, in which case they need to have unique names.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker&lt;/code&gt;:&lt;/strong&gt; The steps of a job occur in an environment called an executor. The common executor in CircleCI is a &lt;a href=&quot;https://www.docker.com/resources/what-container&quot;&gt;Docker container&lt;/a&gt;. It is a &lt;a href=&quot;https://circleci.com/product/#hosting-options&quot;&gt;cloud-hosted&lt;/a&gt; execution environment but other options exist, like a macOS environment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;image&lt;/code&gt;:&lt;/strong&gt; A Docker image is a file used to create a running Docker container. We are using an image that has Python 3.7 preinstalled.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;working_directory&lt;/code&gt;:&lt;/strong&gt; Your repository has to be checked out somewhere on the build server. The working directory represents the file path where the repository will be stored.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;steps&lt;/code&gt;:&lt;/strong&gt; This key marks the start of a list of steps to be performed by the build server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;checkout&lt;/code&gt;:&lt;/strong&gt; The first step the server needs to do is check the source code out to the working directory. This is performed by a special step called &lt;code&gt;checkout&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;run&lt;/code&gt;:&lt;/strong&gt; Executing command-line programs or commands is done inside the &lt;code&gt;command&lt;/code&gt; key. The actual shell commands will be nested within.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;:&lt;/strong&gt; The CircleCI user interface shows you every build step in the form of an expandable section. The title of the section is taken from the value associated with the &lt;code&gt;name&lt;/code&gt; key.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;command&lt;/code&gt;:&lt;/strong&gt; This key represents the command to run via the shell. The &lt;code&gt;|&lt;/code&gt; symbol specifices that what follows is a literal set of commands, one per line, exactly like you&amp;rsquo;d see in a shell/bash script.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can read the &lt;a href=&quot;https://circleci.com/docs/2.0/configuration-reference/&quot;&gt;CircleCI configuration reference&lt;/a&gt; document for more information.&lt;/p&gt;
&lt;p&gt;Our pipeline is very simple and consists of 3 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Checking out the repository&lt;/li&gt;
&lt;li&gt;Installing the dependencies in a virtual environment&lt;/li&gt;
&lt;li&gt;Running the linter and tests while inside the virtual environment&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We now have everything we need to start our pipeline. Log in to your CircleCI account and click on &lt;em&gt;Add Projects&lt;/em&gt;. Find your &lt;em&gt;CalculatorLibrary&lt;/em&gt; repo and click &lt;em&gt;Set Up Project.&lt;/em&gt; Select Python as your language. Since we already have a &lt;code&gt;config.yml&lt;/code&gt;, we can skip the next steps and click &lt;em&gt;Start building.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;CircleCI will take you to the execution dashboard for your job. If you followed all the steps correctly, you should see your job succeed.&lt;/p&gt;
&lt;p&gt;The final version of your &lt;em&gt;CalculatorLibrary&lt;/em&gt; folder should look like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;CalculatorRepository/
|
├── .circleci
├── .git
├── .gitignore
├── README.md
├── calculator.py
├── requirements.txt
└── test_calculator.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Congratulations! You have created your first continuous integration pipeline. Now, every time you push to the master branch, a job will be triggered. You can see a list of your current and past jobs by clicking on &lt;em&gt;Jobs&lt;/em&gt; in the CircleCI sidebar.&lt;/p&gt;
&lt;h3 id=&quot;make-changes&quot;&gt;Make Changes&lt;/h3&gt;
&lt;p&gt;Time to add multiplication to our calculator library.&lt;/p&gt;
&lt;p&gt;This time, we will first add a unit test without writing the function. Without the code, the test will fail, which will also fail the CircleCI job. Add the following code to the end of your &lt;code&gt;test_calculator.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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_multiplication&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;assert&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calculator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;multiply&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;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Push the code to the master branch and see the job fail in CircleCI. This shows that continuous integration works and watches your back if you make a mistake.&lt;/p&gt;
&lt;p&gt;Now add the code to &lt;code&gt;calculator.py&lt;/code&gt; that will make the test pass:&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;multiply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_term&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second_term&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;first_term&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second_term&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Make sure there are two empty spaces between the multiplication function and the previous one, or else your code will fail the linter check.&lt;/p&gt;
&lt;p&gt;The job should be successful this time. This workflow of writing a failing test first and then adding the code to pass the test is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Test-driven_development&quot;&gt;test driven development&lt;/a&gt; (TDD). It&amp;rsquo;s a great way to work because it makes you think about your code structure in advance.&lt;/p&gt;
&lt;p&gt;Now try it on your own. Add a test for the division function, see it fail, and write the function to make the test pass.&lt;/p&gt;
&lt;h3 id=&quot;notifications&quot;&gt;Notifications&lt;/h3&gt;
&lt;p&gt;When working on big applications that have a lot of moving parts, it can take a while for the continuous integration job to run. Most teams set up a notification procedure to let them know if one of their jobs fail. They can continue working while waiting for the job to run.&lt;/p&gt;
&lt;p&gt;The most popular options are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sending an email for each failed build&lt;/li&gt;
&lt;li&gt;Sending failure notifications to a Slack channel &lt;/li&gt;
&lt;li&gt;Displaying failures on a dashboard visible to everyone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By default, CircleCI should send you an email when a job fails.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;You have understood the basics of continuous integration and practiced setting up a pipeline for a simple Python program. This is a big step forward in your journey as a developer. You might be asking yourself, &amp;ldquo;What now?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;To keep things simple, this tutorial skimmed over some big topics. You can grow your skill set immensely by spending some time going more in-depth into each subject. Here are some topics you can look into further.&lt;/p&gt;
&lt;h3 id=&quot;git-workflows&quot;&gt;Git Workflows&lt;/h3&gt;
&lt;p&gt;There is much more to Git than what you used here. Each developer team has a workflow tailored to their specific needs. Most of them include branching strategies and something called &lt;strong&gt;peer review&lt;/strong&gt;. They make changes on branches separate from the &lt;code&gt;master&lt;/code&gt; branch. When you want to merge those changes with &lt;code&gt;master&lt;/code&gt;, other developers must first look at your changes and approve them before you&amp;rsquo;re allowed to merge.&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 want to learn more about different workflows teams use, have a look at the tutorials on &lt;a href=&quot;https://help.github.com/categories/collaborating-with-issues-and-pull-requests/&quot;&gt;GitHub&lt;/a&gt; and &lt;a href=&quot;https://www.atlassian.com/git/tutorials/learn-git-with-bitbucket-cloud&quot;&gt;BitBucket&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If you want to sharpen your Git skills, we have an article called &lt;a href=&quot;https://realpython.com/advanced-git-for-pythonistas/&quot;&gt;Advanced Git Tips for Python Developers&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;dependency-management-and-virtual-environments&quot;&gt;Dependency Management and Virtual Environments&lt;/h3&gt;
&lt;p&gt;Apart from &lt;code&gt;virtualenv&lt;/code&gt;, there are other popular package and environment managers. Some of them deal with just virtual environments, while some handle both package installation and environment management. One of them is Conda:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Conda is an open source package management system and environment management system that runs on Windows, macOS, and Linux. Conda quickly installs, runs and updates packages and their dependencies. Conda easily creates, saves, loads and switches between environments on your local computer. It was designed for Python programs, but it can package and distribute software for any language.&amp;rdquo; (&lt;a href=&quot;https://conda.io/docs/&quot;&gt;Source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Another option is &lt;a href=&quot;https://pipenv.readthedocs.io/en/latest/&quot;&gt;Pipenv&lt;/a&gt;, a younger contender that is rising in popularity among application developers. Pipenv brings together &lt;code&gt;pip&lt;/code&gt; and &lt;code&gt;virtualenv&lt;/code&gt; into a single tool and uses a &lt;code&gt;Pipfile&lt;/code&gt; instead of &lt;code&gt;requirements.txt&lt;/code&gt;. Pipfiles offer deterministic environments and more security. This introduction doesn&amp;rsquo;t do it justice, so check out &lt;a href=&quot;https://realpython.com/pipenv-guide/&quot;&gt;Pipenv: A Guide to the New Python Packaging Tool&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;testing&quot;&gt;Testing&lt;/h3&gt;
&lt;p&gt;Simple unit tests with &lt;code&gt;pytest&lt;/code&gt; are only the tip of the iceberg. There&amp;rsquo;s a whole world out there to explore! Software can be tested on many levels, including integration testing, acceptance testing, regression testing, and so forth. To take your knowledge of testing Python code to the next level, head over to &lt;a href=&quot;https://realpython.com/python-testing/&quot;&gt;Getting Started With Testing in Python&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;packaging&quot;&gt;Packaging&lt;/h3&gt;
&lt;p&gt;In this tutorial, you started to build a library of functions for other developers to use in their project. You need to package that library into a format that is easy to distribute and install using, for example &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Creating an installable package requires a different layout and some additional files like &lt;code&gt;__init__.py&lt;/code&gt; and &lt;code&gt;setup.py&lt;/code&gt;. Read &lt;a href=&quot;https://realpython.com/python-application-layouts/#installable-single-package&quot;&gt;Python Application Layouts: A Reference&lt;/a&gt; for more information on structuring your code.&lt;/p&gt;
&lt;p&gt;To learn how to turn your repository into an installable Python package, read &lt;a href=&quot;https://packaging.python.org/tutorials/packaging-projects/&quot;&gt;Packaging Python Projects&lt;/a&gt; by the &lt;a href=&quot;https://www.pypa.io/en/latest/&quot;&gt;Python Packaging Authority&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;continuous-integration&quot;&gt;Continuous Integration&lt;/h3&gt;
&lt;p&gt;You covered all the basics of CI in this tutorial, using a simple example of Python code. It&amp;rsquo;s common for the final step of a CI pipeline to create a &lt;strong&gt;deployable artifact&lt;/strong&gt;. An artifact represents a finished, packaged unit of work that is ready to be deployed to users or included in complex products.&lt;/p&gt;
&lt;p&gt;For example, to turn your calculator library into a deployable artifact, you would organize it into an installable package. Finally, you would add a step in CircleCI to package the library and store that artifact where other processes can pick it up.&lt;/p&gt;
&lt;p&gt;For more complex applications, you can create a workflow to schedule and connect multiple CI jobs into a single execution. Feel free to explore the &lt;a href=&quot;https://circleci.com/docs/2.0/&quot;&gt;CircleCI documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;continuous-deployment&quot;&gt;Continuous Deployment&lt;/h3&gt;
&lt;p&gt;You can think of continuous deployment as an extension of CI. Once your code is tested and built into a deployable artifact, it is deployed to production, meaning the live application is updated with your changes. One of the goals is to minimize lead time, the time elapsed between writing a new line of code and putting it in front of users.&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; To add a bit of confusion to the mix, the acronym CD is not unique. It can also mean Continuous Delivery, which is almost the same as continuous deployment but has a manual verification step between integration and deployment. You can integrate your code at any time but have to push a button to release it to the live application.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Most companies use CI/CD in tandem, so it&amp;rsquo;s worth your time to learn more about &lt;a href=&quot;https://continuousdelivery.com/&quot;&gt;Continuous Delivery/Deployment&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;overview-of-continuous-integration-services&quot;&gt;Overview of Continuous Integration Services&lt;/h2&gt;
&lt;p&gt;You have used CircleCI, one of the most popular continuous integration services. However, this is a big market with a lot of strong contenders. CI products fall into two basic categories: remote and self-hosted services.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jenkins.io/&quot;&gt;Jenkins&lt;/a&gt; is the most popular self-hosted solution. It is open-source and flexible, and the community has developed a lot of extensions.&lt;/p&gt;
&lt;p&gt;In terms of remote services, there are many popular options like &lt;a href=&quot;https://travis-ci.org/&quot;&gt;TravisCI&lt;/a&gt;, &lt;a href=&quot;https://codeship.com/&quot;&gt;CodeShip&lt;/a&gt;, and &lt;a href=&quot;https://semaphoreci.com/&quot;&gt;Semaphore&lt;/a&gt;. Big enterprises often have their custom solutions, and they sell them as a service, such as &lt;a href=&quot;https://aws.amazon.com/codepipeline/&quot;&gt;AWS CodePipeline&lt;/a&gt;, &lt;a href=&quot;https://visualstudio.microsoft.com/tfs/&quot;&gt;Microsoft Team Foundation Server&lt;/a&gt;, and Oracle&amp;rsquo;s &lt;a href=&quot;http://hudson-ci.org/&quot;&gt;Hudson&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Which option you choose depends on the platform and features you and your team need. For a more detailed breakdown,  have a look at &lt;a href=&quot;https://www.g2crowd.com/categories/continuous-integration&quot;&gt;Best CI Software&lt;/a&gt; by G2Crowd.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With the knowledge from this tutorial under your belt, you can now answer the following questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What is continuous integration?&lt;/li&gt;
&lt;li&gt;Why is continuous integration important?&lt;/li&gt;
&lt;li&gt;What are the core practices of continuous integration?&lt;/li&gt;
&lt;li&gt;How can I set up continuous integration for my Python project?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You have acquired a programming superpower! Understanding the philosophy and practice of continuous integration will make you a valuable member of any team. Awesome work!&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>Memory Management in Python</title>
      <id>https://realpython.com/python-memory-management/</id>
      <link href="https://realpython.com/python-memory-management/"/>
      <updated>2018-11-21T14:00:00+00:00</updated>
      <summary>Get ready for a deep dive into the internals of Python to understand how it handles memory management. By the end of this article, you’ll know more about low-level computing, understand how Python abstracts lower-level operations, and find out about Python’s internal memory management algorithms.</summary>
      <content type="html">
        &lt;p&gt;Ever wonder how Python handles your data behind the scenes? How are your variables stored in memory? When do they get deleted?&lt;/p&gt;
&lt;p&gt;In this article, we&amp;rsquo;re going to do a deep dive into the internals of Python to understand how it handles memory management.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this article, you&amp;rsquo;ll:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learn more about low-level computing, specifically as relates to memory&lt;/li&gt;
&lt;li&gt;Understand how Python abstracts lower-level operations&lt;/li&gt;
&lt;li&gt;Learn about Python&amp;rsquo;s internal memory management algorithms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding Python&amp;rsquo;s internals will also give you better insight into some of Python&amp;rsquo;s behaviors. Hopefully, you&amp;rsquo;ll gain a new appreciation for Python as well. So much logic is happening behind the scenes to ensure your program works the way you expect.&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;memory-is-an-empty-book&quot;&gt;Memory Is an Empty Book&lt;/h2&gt;
&lt;p&gt;You can begin by thinking of a computer&amp;rsquo;s memory as an empty book intended for short stories. There&amp;rsquo;s nothing written on the pages yet. Eventually, different authors will come along. Each author wants some space to write their story in.&lt;/p&gt;
&lt;p&gt;Since they aren&amp;rsquo;t allowed to write over each other, they must be careful about which pages they write in. Before they begin writing, they consult the manager of the book. The manager then decides where in the book they&amp;rsquo;re allowed to write.&lt;/p&gt;
&lt;p&gt;Since this book is around for a long time, many of the stories in it are no longer relevant. When no one reads or references the stories, they are removed to make room for new stories.&lt;/p&gt;
&lt;p&gt;In essence, computer memory is like that empty book. In fact, it&amp;rsquo;s common to call fixed-length contiguous blocks of memory &lt;strong&gt;pages&lt;/strong&gt;, so this analogy holds pretty well.&lt;/p&gt;
&lt;p&gt;The authors are like different applications or processes that need to store data in memory. The manager, who decides where the authors can write in the book, plays the role of a memory manager of sorts. The person who removed the old stories to make room for new ones is a garbage collector.&lt;/p&gt;
&lt;h2 id=&quot;memory-management-from-hardware-to-software&quot;&gt;Memory Management: From Hardware to Software&lt;/h2&gt;
&lt;p&gt;Memory management is the process by which applications read and write data. A memory manager determines where to put an application&amp;rsquo;s data. Since there&amp;rsquo;s a finite chunk of memory, like the pages in our book analogy, the manager has to find some free space and provide it to the application. This process of providing memory is generally called memory &lt;strong&gt;allocation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On the flip side, when data is no longer needed, it can be deleted, or &lt;strong&gt;freed&lt;/strong&gt;. But freed to where? Where did this &amp;ldquo;memory&amp;rdquo; come from?&lt;/p&gt;
&lt;p&gt;Somewhere in your computer, there&amp;rsquo;s a physical device storing data when you&amp;rsquo;re running your Python programs. There are many layers of abstraction that the Python code goes through before the objects actually get to the hardware though.&lt;/p&gt;
&lt;p&gt;One of the main layers above the hardware (such as RAM or a hard drive) is the operating system (OS). It carries out (or denies) requests to read and write memory.&lt;/p&gt;
&lt;p&gt;Above the OS, there are applications, one of which is the default Python implementation (included in your OS or downloaded from &lt;a href=&quot;https://www.python.org/&quot;&gt;python.org&lt;/a&gt;.) Memory management for your Python code is handled by the Python application. The algorithms and structures that the Python application uses for memory management is the focus of this article.&lt;/p&gt;
&lt;h2 id=&quot;the-default-python-implementation&quot;&gt;The Default Python Implementation&lt;/h2&gt;
&lt;p&gt;The default Python implementation, CPython, is actually written in the C programming language.&lt;/p&gt;
&lt;p&gt;When I first heard this, it blew my mind. A language that&amp;rsquo;s written in another language?! Well, not really, but sort of.&lt;/p&gt;
&lt;p&gt;The Python language is defined in a &lt;a href=&quot;https://docs.python.org/3/reference/index.html&quot;&gt;reference manual&lt;/a&gt; written in English. However, that manual isn&amp;rsquo;t all that useful by itself. You still need something to interpret written code based on the rules in the manual.&lt;/p&gt;
&lt;p&gt;You also need something to actually execute interpreted code on a computer. The default Python implementation fulfills both of those requirements. It converts your Python code into instructions that it then runs on a virtual machine.&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; Virtual machines are like physical computers, but they are implemented in software. They typically process basic instructions similar to &lt;a href=&quot;https://en.wikipedia.org/wiki/Assembly_language&quot;&gt;Assembly instructions&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Python is an interpreted programming language. Your Python code actually gets compiled down to more computer-readable instructions called &lt;a href=&quot;https://docs.python.org/3/glossary.html#term-bytecode&quot;&gt;bytecode&lt;/a&gt;. These instructions get &lt;strong&gt;interpreted&lt;/strong&gt; by a virtual machine when you run your code.&lt;/p&gt;
&lt;p&gt;Have you ever seen a &lt;code&gt;.pyc&lt;/code&gt; file or a &lt;code&gt;__pycache__&lt;/code&gt; folder? That&amp;rsquo;s the bytecode that gets interpreted by the virtual machine.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s important to note that there are implementations other than CPython. &lt;a href=&quot;http://ironpython.net/&quot;&gt;IronPython&lt;/a&gt; compiles down to run on Microsoft&amp;rsquo;s Common Language Runtime. &lt;a href=&quot;http://www.jython.org/&quot;&gt;Jython&lt;/a&gt; compiles down to Java bytecode to run on the Java Virtual Machine. Then there&amp;rsquo;s &lt;a href=&quot;https://pypy.org/&quot;&gt;PyPy&lt;/a&gt;, but that deserves its own entire article, so I&amp;rsquo;ll just mention it in passing.&lt;/p&gt;
&lt;p&gt;For the purposes of this article, I&amp;rsquo;ll focus on the memory management done by the default implementation of Python, CPython.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: While a lot of this information will carry through to new versions of Python, things may change in the future. Note that the referenced version for this article is the current latest version of Python, &lt;a href=&quot;https://realpython.com/python37-new-features/&quot;&gt;&lt;code&gt;3.7&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Okay, so CPython is written in C, and it interprets Python bytecode. What does this have to do with memory management? Well, the memory management algorithms and structures exist in the CPython code, in C. To understand the memory management of Python, you have to get a basic understanding of CPython itself.&lt;/p&gt;
&lt;p&gt;CPython is written in C, which does not natively support object-oriented programming. Because of that, there are quite a bit of interesting designs in the CPython code.&lt;/p&gt;
&lt;p&gt;You may have heard that everything in Python is an object, even types such as &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;str&lt;/code&gt;. Well, it&amp;rsquo;s true on an implementation level in CPython. There is a &lt;code&gt;struct&lt;/code&gt; called a &lt;code&gt;PyObject&lt;/code&gt;, which every other object in CPython uses.&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; A &lt;code&gt;struct&lt;/code&gt;, or &lt;strong&gt;structure&lt;/strong&gt;, in C is a custom data type that groups together different data types. To compare to object-oriented languages, it&amp;rsquo;s like a class with attributes and no methods.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;PyObject&lt;/code&gt;, the grand-daddy of all objects in Python, contains only two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ob_refcnt&lt;/code&gt;:&lt;/strong&gt; reference count&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ob_type&lt;/code&gt;:&lt;/strong&gt; pointer to another type&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The reference count is used for garbage collection. Then you have a pointer to the actual object type. That object type is just another &lt;code&gt;struct&lt;/code&gt; that describes a Python object (such as a &lt;code&gt;dict&lt;/code&gt; or &lt;code&gt;int&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Each object has its own object-specific memory allocator that knows how to get the memory to store that object. Each object also has an object-specific memory deallocator that &amp;ldquo;frees&amp;rdquo; the memory once it&amp;rsquo;s no longer needed.&lt;/p&gt;
&lt;p&gt;However, there&amp;rsquo;s an important factor in all this talk about allocating and freeing memory. Memory is a shared resource on the computer, and bad things can happen if two different processes try to write to the same location at the same time.&lt;/p&gt;
&lt;h2 id=&quot;the-global-interpreter-lock-gil&quot;&gt;The Global Interpreter Lock (GIL)&lt;/h2&gt;
&lt;p&gt;The GIL is a solution to the common problem of dealing with shared resources, like memory in a computer. When two threads try to modify the same resource at the same time, they can step on each other&amp;rsquo;s toes. The end result can be a garbled mess where neither of the threads ends up with what they wanted.&lt;/p&gt;
&lt;p&gt;Consider the book analogy again. Suppose that two authors stubbornly decide that it&amp;rsquo;s their turn to write. Not only that, but they both need to write on the same page of the book at the same time.&lt;/p&gt;
&lt;p&gt;They each ignore the other&amp;rsquo;s attempt to craft a story and begin writing on the page. The end result is two stories on top of each other, which makes the whole page completely unreadable.&lt;/p&gt;
&lt;p&gt;One solution to this problem is a single, global lock on the interpreter when a thread is interacting with the shared resource (the page in the book). In other words, only one author can write at a time.&lt;/p&gt;
&lt;p&gt;Python&amp;rsquo;s GIL accomplishes this by locking the entire interpreter, meaning that it&amp;rsquo;s not possible for another thread to step on the current one. When CPython handles memory, it uses the GIL to ensure that it does so safely.&lt;/p&gt;
&lt;p&gt;There are pros and cons to this approach, and the GIL is heavily debated in the Python community. To read more about the GIL, I suggest checking out &lt;a href=&quot;https://realpython.com/python-gil/&quot;&gt;What is the Python Global Interpreter Lock (GIL)?&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;garbage-collection&quot;&gt;Garbage Collection&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s revisit the book analogy and assume that some of the stories in the book are getting very old. No one is reading or referencing those stories anymore. If no one is reading something or referencing it in their own work, you could get rid of it to make room for new writing.&lt;/p&gt;
&lt;p&gt;That old, unreferenced writing could be compared to an object in Python whose reference count has dropped to &lt;code&gt;0&lt;/code&gt;. Remember that every object in Python has a reference count and a pointer to a type.&lt;/p&gt;
&lt;p&gt;The reference count gets increased for a few different reasons. For example, the reference count will increase if you assign it to another variable:&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;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;1&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;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Reference count = 1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;more_numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Reference count = 2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It will also increase if you pass the object as an argument:&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;total&lt;/span&gt; &lt;span class=&quot;o&quot;&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;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a final example, the reference count will increase if you include the object in a 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;matrix&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;numbers&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;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Python allows you to inspect the current reference count of an object with the &lt;code&gt;sys&lt;/code&gt; module. You can use &lt;code&gt;sys.getrefcount(numbers)&lt;/code&gt;, but keep in mind that passing in the object to &lt;code&gt;getrefcount()&lt;/code&gt; increases the reference count by &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In any case, if the object is still required to hang around in your code, its reference count is greater than &lt;code&gt;0&lt;/code&gt;. Once it drops to &lt;code&gt;0&lt;/code&gt;, the object has a specific deallocation function that is called which &amp;ldquo;frees&amp;rdquo; the memory so that other objects can use it.&lt;/p&gt;
&lt;p&gt;But what does it mean to &amp;ldquo;free&amp;rdquo; the memory, and how do other objects use it? Let&amp;rsquo;s jump right into CPython&amp;rsquo;s memory management.&lt;/p&gt;
&lt;h2 id=&quot;cpythons-memory-management&quot;&gt;CPython&amp;rsquo;s Memory Management&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;re going to dive deep into CPython&amp;rsquo;s memory architecture and algorithms, so buckle up.&lt;/p&gt;
&lt;p&gt;As mentioned before, there are layers of abstraction from the physical hardware to CPython. The operating system (OS) abstracts the physical memory and creates a virtual memory layer that applications (including Python) can access.&lt;/p&gt;
&lt;p&gt;An OS-specific virtual memory manager carves out a chunk of memory for the Python process. The darker gray boxes in the image below are now owned by the Python process.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/memory_management.92ad564ec680.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/memory_management.92ad564ec680.png&quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management.92ad564ec680.png&amp;amp;w=240&amp;amp;sig=8ae5dcd1eac26ef6bd12a723ad544da2fbbe2603 240w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management.92ad564ec680.png&amp;amp;w=480&amp;amp;sig=2c7822bde29d5ac26b3459f120dccd31049bac25 480w, https://files.realpython.com/media/memory_management.92ad564ec680.png 960w&quot; sizes=&quot;75vw&quot; alt=&quot;Blocks to Show Different Areas of Memory with Object Memory Highlighted&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Python uses a portion of the memory for internal use and non-object memory. The other portion is dedicated to object storage (your &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;, and the like). Note that this was somewhat simplified. If you want the full picture, you can check out the &lt;a href=&quot;https://github.com/python/cpython/blob/7d6ddb96b34b94c1cbdf95baa94492c48426404e/Objects/obmalloc.c&quot;&gt;CPython source code&lt;/a&gt;, where all this memory management happens.&lt;/p&gt;
&lt;p&gt;CPython has an object allocator that is responsible for allocating memory within the object memory area. This object allocator is where most of the magic happens. It gets called every time a new object needs space allocated or deleted.&lt;/p&gt;
&lt;p&gt;Typically, the adding and removing of data for Python objects like &lt;code&gt;list&lt;/code&gt; and &lt;code&gt;int&lt;/code&gt; doesn&amp;rsquo;t involve too much data at a time. So the design of the allocator is tuned to work well with small amounts of data at a time. It also tries not to allocate memory until it&amp;rsquo;s absolutely required.&lt;/p&gt;
&lt;p&gt;The comments in the &lt;a href=&quot;https://github.com/python/cpython/blob/7d6ddb96b34b94c1cbdf95baa94492c48426404e/Objects/obmalloc.c&quot;&gt;source code&lt;/a&gt; describe the allocator as &amp;ldquo;a fast, special-purpose memory allocator for small blocks, to be used on top of a general-purpose malloc.&amp;rdquo; In this case, &lt;code&gt;malloc&lt;/code&gt; is C&amp;rsquo;s library function for memory allocation.&lt;/p&gt;
&lt;p&gt;Now we&amp;rsquo;ll look at CPython&amp;rsquo;s memory allocation strategy. First, we&amp;rsquo;ll talk about the 3 main pieces and how they relate to each other.&lt;/p&gt;
&lt;p&gt;Arenas are the largest chunks of memory and are aligned on a page boundary in memory. A page boundary is the edge of a fixed-length contiguous chunk of memory that the OS uses. Python assumes the system&amp;rsquo;s page size is 256 kilobytes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/memory_management_5.394b85976f34.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/memory_management_5.394b85976f34.png&quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management_5.394b85976f34.png&amp;amp;w=240&amp;amp;sig=e5064c6488fcd49b258447d8015dc7af5ebf8f35 240w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management_5.394b85976f34.png&amp;amp;w=480&amp;amp;sig=421507f901c53651ba6750122488a4d2dc1c5c53 480w, https://files.realpython.com/media/memory_management_5.394b85976f34.png 960w&quot; sizes=&quot;75vw&quot; alt=&quot;Book with Page filled with Arena, Pools, and Block&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Within the arenas are pools, which are one virtual memory page (4 kilobytes). These are like the pages in our book analogy. These pools are fragmented into smaller blocks of memory.&lt;/p&gt;
&lt;p&gt;All the blocks in a given pool are of the same &amp;ldquo;size class.&amp;rdquo; A size class defines a specific block size, given some amount of requested data. The chart below is taken directly from the &lt;a href=&quot;https://github.com/python/cpython/blob/7d6ddb96b34b94c1cbdf95baa94492c48426404e/Objects/obmalloc.c&quot;&gt;source code&lt;/a&gt; comments:&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-center&quot;&gt;Request in bytes&lt;/th&gt;
&lt;th class=&quot;text-center&quot;&gt;Size of allocated block&lt;/th&gt;
&lt;th class=&quot;text-center&quot;&gt;Size class idx&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;1-8&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;8&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;9-16&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;16&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;17-24&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;24&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;25-32&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;32&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;33-40&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;40&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;41-48&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;48&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;49-56&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;56&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;57-64&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;64&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;65-72&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;72&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;&amp;hellip;&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;&amp;hellip;&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;&amp;hellip;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;497-504&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;504&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;62&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-center&quot;&gt;505-512&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;512&lt;/td&gt;
&lt;td class=&quot;text-center&quot;&gt;63&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;For example, if 42 bytes are requested, the data would be placed into a size 48-byte block.&lt;/p&gt;
&lt;h3 id=&quot;pools&quot;&gt;Pools&lt;/h3&gt;
&lt;p&gt;Pools are composed of blocks from a single size class. Each pool maintains a double-linked list to other pools of the same size class. In that way, the algorithm can easily find available space for a given block size, even across different pools.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;usedpools&lt;/code&gt; list tracks all the pools that have some space available for data for each size class. When a given block size is requested, the algorithm checks this &lt;code&gt;usedpools&lt;/code&gt; list for the list of pools for that block size.&lt;/p&gt;
&lt;p&gt;Pools themselves must be in one of 3 states: &lt;code&gt;used&lt;/code&gt;, &lt;code&gt;full&lt;/code&gt;, or &lt;code&gt;empty&lt;/code&gt;. A &lt;code&gt;used&lt;/code&gt; pool has available blocks for data to be stored. A &lt;code&gt;full&lt;/code&gt; pool&amp;rsquo;s blocks are all allocated and contain data. An &lt;code&gt;empty&lt;/code&gt; pool has no data stored and can be assigned any size class for blocks when needed.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;freepools&lt;/code&gt; list keeps track of all the pools in the &lt;code&gt;empty&lt;/code&gt; state. But when do empty pools get used?&lt;/p&gt;
&lt;p&gt;Assume your code needs an 8-byte chunk of memory. If there are no pools in &lt;code&gt;usedpools&lt;/code&gt; of the 8-byte size class, a fresh &lt;code&gt;empty&lt;/code&gt; pool is initialized to store 8-byte blocks. This new pool then gets added to the &lt;code&gt;usedpools&lt;/code&gt; list so it can be used for future requests.&lt;/p&gt;
&lt;p&gt;Say a &lt;code&gt;full&lt;/code&gt; pool frees some of its blocks because the memory is no longer needed. That pool would get added back to the &lt;code&gt;usedpools&lt;/code&gt; list for its size class.&lt;/p&gt;
&lt;p&gt;You can see now how pools can move between these states (and even memory size classes) freely with this algorithm.&lt;/p&gt;
&lt;h3 id=&quot;blocks&quot;&gt;Blocks&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/memory_management_3.52bffbf302d3.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/memory_management_3.52bffbf302d3.png&quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management_3.52bffbf302d3.png&amp;amp;w=240&amp;amp;sig=46754d3921f47bdce1169c27209c11393302a30d 240w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management_3.52bffbf302d3.png&amp;amp;w=480&amp;amp;sig=7a77cec96b579b00b2deeb7fdfc1658425335ae8 480w, https://files.realpython.com/media/memory_management_3.52bffbf302d3.png 960w&quot; sizes=&quot;75vw&quot; alt=&quot;Diagram of Used, Full, and Emtpy Pools&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As seen in the diagram above, pools contain a pointer to their &amp;ldquo;free&amp;rdquo; blocks of memory. There&amp;rsquo;s a slight nuance to the way this works. This allocator &amp;ldquo;strives at all levels (arena, pool, and block) never to touch a piece of memory until it&amp;rsquo;s actually needed,&amp;rdquo; according to the comments in the source code.&lt;/p&gt;
&lt;p&gt;That means that a pool can have blocks in 3 states. These states can be defined as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;untouched&lt;/code&gt;:&lt;/strong&gt; a portion of memory that has not been allocated&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;free&lt;/code&gt;:&lt;/strong&gt; a portion of memory that was allocated but later made &amp;ldquo;free&amp;rdquo; by CPython and that no longer contains relevant data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;allocated&lt;/code&gt;:&lt;/strong&gt; a portion of memory that actually contains relevant data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;freeblock&lt;/code&gt; pointer points to a singly linked list of free blocks of memory. In other words, a list of available places to put data. If more than the available free blocks are needed, the allocator will get some &lt;code&gt;untouched&lt;/code&gt; blocks in the pool.&lt;/p&gt;
&lt;p&gt;As the memory manager makes blocks &amp;ldquo;free,&amp;rdquo; those now &lt;code&gt;free&lt;/code&gt; blocks get added to the front of the &lt;code&gt;freeblock&lt;/code&gt; list. The actual list may not be contiguous blocks of memory, like the first nice diagram. It may look something like the diagram below:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/memory_management_4.4a30dfa2d111.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/memory_management_4.4a30dfa2d111.png&quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management_4.4a30dfa2d111.png&amp;amp;w=240&amp;amp;sig=c5b74794db710a01a492bc27ecafe1f754da1176 240w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management_4.4a30dfa2d111.png&amp;amp;w=480&amp;amp;sig=a241496c13638e02a222e81e3503ac3657f6a33d 480w, https://files.realpython.com/media/memory_management_4.4a30dfa2d111.png 960w&quot; sizes=&quot;75vw&quot; alt=&quot;Diagrams showing freeblock Singly-Linked List Pointing to Free Blocks in a Pool&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;arenas&quot;&gt;Arenas&lt;/h3&gt;
&lt;p&gt;Arenas contain pools. Those pools can be &lt;code&gt;used&lt;/code&gt;, &lt;code&gt;full&lt;/code&gt;, or &lt;code&gt;empty&lt;/code&gt;. Arenas themselves don&amp;rsquo;t have as explicit states as pools do though.&lt;/p&gt;
&lt;p&gt;Arenas are instead organized into a doubly linked list called &lt;code&gt;usable_arenas&lt;/code&gt;. The list is sorted by the number of free pools available. The fewer free pools, the closer the arena is to the front of the list.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/memory_management_6.60e9761bc158.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/memory_management_6.60e9761bc158.png&quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management_6.60e9761bc158.png&amp;amp;w=240&amp;amp;sig=bdddc79834b48ccf53ed2635cc70e2a7b3e54619 240w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/memory_management_6.60e9761bc158.png&amp;amp;w=480&amp;amp;sig=75696bff7726838f316b61749836bd54dadab12b 480w, https://files.realpython.com/media/memory_management_6.60e9761bc158.png 960w&quot; sizes=&quot;75vw&quot; alt=&quot;usable_areas Doubly-linked List of Arenas&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This means that the arena that is the most full of data will be selected to place new data into. But why not the opposite? Why not place data where there&amp;rsquo;s the most available space?&lt;/p&gt;
&lt;p&gt;This brings us to the idea of truly freeing memory. You&amp;rsquo;ll notice that I&amp;rsquo;ve been saying &amp;ldquo;free&amp;rdquo; in quotes quite a bit. The reason is that when a block is deemed &amp;ldquo;free&amp;rdquo;, that memory is not actually freed back to the operating system. The Python process keeps it allocated and will use it later for new data. Truly freeing memory returns it to the operating system to use.&lt;/p&gt;
&lt;p&gt;Arenas are the only things that can truly be freed. So, it stands to reason that those arenas that are closer to being empty should be allowed to become empty. That way, that chunk of memory can be truly freed, reducing the overall memory footprint of your Python program.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Memory management is an integral part of working with computers. Python handles nearly all of it behind the scenes, for better or for worse.&lt;/p&gt;
&lt;p&gt;In this article, you learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What memory management is and why it&amp;rsquo;s important&lt;/li&gt;
&lt;li&gt;How the default Python implementation, CPython, is written in the C programming language&lt;/li&gt;
&lt;li&gt;How the data structures and algorithms work together in CPython&amp;rsquo;s memory management to handle your data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Python abstracts away a lot of the gritty details of working with computers. This gives you the power to work on a higher level to develop your code without the headache of worrying about how and where all those bytes are getting stored.&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>Interactive Data Visualization in Python With Bokeh</title>
      <id>https://realpython.com/python-data-visualization-bokeh/</id>
      <link href="https://realpython.com/python-data-visualization-bokeh/"/>
      <updated>2018-11-19T14:00:00+00:00</updated>
      <summary>This Python tutorial will get you up and running with Bokeh, using examples and a real-world dataset. You&#39;ll learn how to visualize your data, customize and organize your visualizations, and add interactivity.</summary>
      <content type="html">
        &lt;p&gt;Bokeh prides itself on being a library for &lt;em&gt;interactive&lt;/em&gt; data visualization. &lt;/p&gt;
&lt;p&gt;Unlike popular counterparts in the Python visualization space, like Matplotlib and Seaborn, Bokeh renders its graphics using HTML and JavaScript. This makes it a great candidate for building web-based dashboards and applications. However, it&amp;rsquo;s an equally powerful tool for exploring and understanding your data or creating beautiful custom charts for a project or report.&lt;/p&gt;
&lt;p&gt;Using a number of examples on a real-world dataset, the goal of this tutorial is to get you up and running with Bokeh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You&amp;rsquo;ll learn how to:&lt;/strong&gt; &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Transform your data&lt;/strong&gt; into visualizations, using Bokeh&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customize and organize&lt;/strong&gt; your visualizations &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add interactivity&lt;/strong&gt; to your visualizations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So let&amp;rsquo;s jump in.&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;from-data-to-visualization&quot;&gt;From Data to Visualization&lt;/h2&gt;
&lt;p&gt;Building a visualization with Bokeh involves the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prepare the data&lt;/li&gt;
&lt;li&gt;Determine where the visualization will be rendered&lt;/li&gt;
&lt;li&gt;Set up the figure(s)&lt;/li&gt;
&lt;li&gt;Connect to and draw your data&lt;/li&gt;
&lt;li&gt;Organize the layout&lt;/li&gt;
&lt;li&gt;Preview and save your beautiful data creation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s explore each step in more detail. &lt;/p&gt;
&lt;h3 id=&quot;prepare-the-data&quot;&gt;Prepare the Data&lt;/h3&gt;
&lt;p&gt;Any good data visualization starts with&amp;mdash;you guessed it&amp;mdash;data. If you need a quick refresher on handling data in Python, definitely check out the &lt;a href=&quot;https://realpython.com/tutorials/data-science/&quot;&gt;growing number of excellent &lt;em&gt;Real Python&lt;/em&gt; tutorials on the subject&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This step commonly involves data handling libraries like &lt;a href=&quot;https://pandas.pydata.org&quot;&gt;Pandas&lt;/a&gt; and &lt;a href=&quot;http://www.numpy.org/#&quot;&gt;Numpy&lt;/a&gt; and is all about taking the required steps to transform it into a form that is best suited for your intended visualization. &lt;/p&gt;
&lt;h3 id=&quot;determine-where-the-visualization-will-be-rendered&quot;&gt;Determine Where the Visualization Will Be Rendered&lt;/h3&gt;
&lt;p&gt;At this step, you&amp;rsquo;ll determine how you want to generate and ultimately view your visualization. In this tutorial, you&amp;rsquo;ll learn about two common options that Bokeh provides: generating a static HTML file and rendering your visualization inline in a &lt;a href=&quot;http://jupyter.org&quot;&gt;Jupyter Notebook&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;set-up-the-figures&quot;&gt;Set up the Figure(s)&lt;/h3&gt;
&lt;p&gt;From here, you&amp;rsquo;ll assemble your figure, preparing the canvas for your visualization. In this step, you can customize everything from the titles to the tick marks. You can also set up a suite of tools that can enable various user interactions with your visualization.&lt;/p&gt;
&lt;h3 id=&quot;connect-to-and-draw-your-data&quot;&gt;Connect to and Draw Your Data&lt;/h3&gt;
&lt;p&gt;Next, you&amp;rsquo;ll use Bokeh&amp;rsquo;s multitude of renderers to give shape to your data. Here, you have the flexibility to draw your data from scratch using the many available marker and shape options, all of which are easily customizable. This functionality gives you incredible creative freedom in representing your data. &lt;/p&gt;
&lt;p&gt;Additionally, Bokeh has some built-in functionality for building things like &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/categorical.html#stacked&quot;&gt;stacked bar charts&lt;/a&gt; and plenty of examples for creating more advanced visualizations like &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/graph.html&quot;&gt;network graphs&lt;/a&gt; and &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/geo.html&quot;&gt;maps&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;organize-the-layout&quot;&gt;Organize the Layout&lt;/h3&gt;
&lt;p&gt;If you need more than one figure to express your data, Bokeh&amp;rsquo;s got you covered. Not only does Bokeh offer the standard grid-like layout options, but it also allows you to easily organize your visualizations into a tabbed layout in just a few lines of code. &lt;/p&gt;
&lt;p&gt;In addition, your plots can be quickly linked together, so a selection on one will be reflected on any combination of the others. &lt;/p&gt;
&lt;h3 id=&quot;preview-and-save-your-beautiful-data-creation&quot;&gt;Preview and Save Your Beautiful Data Creation&lt;/h3&gt;
&lt;p&gt;Finally, it&amp;rsquo;s time to see what you created. &lt;/p&gt;
&lt;p&gt;Whether you&amp;rsquo;re viewing your visualization in a browser or notebook, you&amp;rsquo;ll be able to explore your visualization, examine your customizations, and play with any interactions that were added. &lt;/p&gt;
&lt;p&gt;If you like what you see, you can save your visualization to an image file. Otherwise, you can revisit the steps above as needed to bring your data vision to reality. &lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it! Those six steps are the building blocks for a tidy, flexible template that can be used to take your data from the table to the big screen:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Bokeh Visualization Template&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;This template is a general outline for turning your data into a &lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;visualization using Bokeh.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Data handling&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pandas&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pd&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;np&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Bokeh libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_notebook&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.layouts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&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;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gridplot&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models.widgets&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tabs&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;c1&quot;&gt;# Prepare the data&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Determine where the visualization will be rendered&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;filename.html&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Render to static HTML, or &lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_notebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Render inline in a Jupyter Notebook&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Set up the figure(s)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Instantiate a figure() object&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Connect to and draw the data&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Organize the layout&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Preview and save &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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# See what I made, and save if I like it&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some common code snippets that are found in each step are previewed above, and you&amp;rsquo;ll see how to fill out the rest as you move through the rest of the tutorial!&lt;/p&gt;
&lt;h2 id=&quot;generating-your-first-figure&quot;&gt;Generating Your First Figure&lt;/h2&gt;
&lt;p&gt;There are &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/concepts.html#output-methods&quot;&gt;multiple ways to output your visualization&lt;/a&gt; in Bokeh. In this tutorial, you&amp;rsquo;ll see these two options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;output_file(&#39;filename.html&#39;)&lt;/code&gt;&lt;/strong&gt; will write the visualization to a static HTML file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;output_notebook()&lt;/code&gt;&lt;/strong&gt; will render your visualization directly in a Jupyter Notebook.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;rsquo;s important to note that neither function will actually show you the visualization. That doesn&amp;rsquo;t happen until &lt;code&gt;show()&lt;/code&gt; is called. However, they will ensure that, when &lt;code&gt;show()&lt;/code&gt; is called, the visualization appears where you intend it to.&lt;/p&gt;
&lt;p&gt;By calling both &lt;code&gt;output_file()&lt;/code&gt; and &lt;code&gt;output_notebook()&lt;/code&gt; in the same execution, the visualization will be rendered both to a static HTML file and inline in the notebook. However, if for whatever reason you run multiple &lt;code&gt;output_file()&lt;/code&gt; commands in the same execution, only the last one will be used for rendering. &lt;/p&gt;
&lt;p&gt;This is a great opportunity to give you your first glimpse at a default Bokeh &lt;code&gt;figure()&lt;/code&gt; using &lt;code&gt;output_file()&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;c1&quot;&gt;# Bokeh Libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# The figure will be rendered in a static HTML file called output_file_test.html&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;output_file_test.html&amp;#39;&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;Empty Bokeh Figure&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Set up a generic figure() object&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# See what it looks like&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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/output_file_example.44c5ca494d41.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/output_file_example.44c5ca494d41.png&quot; width=&quot;1280&quot; height=&quot;1330&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/output_file_example.44c5ca494d41.png&amp;amp;w=320&amp;amp;sig=a9fbf061bba54fe1fea956b1c906ba5d6107cca1 320w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/output_file_example.44c5ca494d41.png&amp;amp;w=640&amp;amp;sig=608b85472bc31d57f9a651ec03cf5432505a6bc5 640w, https://files.realpython.com/media/output_file_example.44c5ca494d41.png 1280w&quot; sizes=&quot;75vw&quot; alt=&quot;output_file()&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see, a new browser window opened with a tab called &lt;em&gt;Empty Bokeh Figure&lt;/em&gt; and an empty figure. Not shown is the file generated with the name &lt;em&gt;output_file_test.html&lt;/em&gt; in your current working directory. &lt;/p&gt;
&lt;p&gt;If you were to run the same code snippet with &lt;code&gt;output_notebook()&lt;/code&gt; in place of &lt;code&gt;output_file()&lt;/code&gt;,  assuming you have a Jupyter Notebook fired up and ready to go, you will get 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;c1&quot;&gt;# Bokeh Libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_notebook&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# The figure will be right in my Jupyter Notebook&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_notebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Set up a generic figure() object&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# See what it looks like&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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/output_notebook_example.94b3a3850c89.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/output_notebook_example.94b3a3850c89.png&quot; width=&quot;1770&quot; height=&quot;1552&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/output_notebook_example.94b3a3850c89.png&amp;amp;w=442&amp;amp;sig=0048fc3e5a4c917e211125799635ee9a386c45c3 442w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/output_notebook_example.94b3a3850c89.png&amp;amp;w=885&amp;amp;sig=5697d3ccdf618d23b17b04f13096aeb1bc3dca35 885w, https://files.realpython.com/media/output_notebook_example.94b3a3850c89.png 1770w&quot; sizes=&quot;75vw&quot; alt=&quot;output_notebook()&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the result is the same, just rendered in a different location. &lt;/p&gt;
&lt;p&gt;More information about both &lt;code&gt;output_file()&lt;/code&gt; and &lt;code&gt;output_notebook()&lt;/code&gt; can be found in the &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/reference/io.html#bokeh-io-output&quot;&gt;Bokeh official docs&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; Sometimes, when rendering multiple visualizations sequentially, you&amp;rsquo;ll see that past renders are not being cleared with each execution. If you experience this, import and run the following between executions:&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;# Import reset_output (only needed once) &lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reset_output&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Use reset_output() between subsequent show() calls, as needed&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;reset_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;Before moving on, you may have noticed that the default Bokeh figure comes pre-loaded with a toolbar. This is an important sneak preview into the interactive elements of Bokeh that come right out of the box. You&amp;rsquo;ll find out more about the toolbar and how to configure it in the &lt;a href=&quot;#adding-interaction&quot;&gt;Adding Interaction&lt;/a&gt; section at the end of this tutorial. &lt;/p&gt;
&lt;h2 id=&quot;getting-your-figure-ready-for-data&quot;&gt;Getting Your Figure Ready for Data&lt;/h2&gt;
&lt;p&gt;Now that you know how to create and view a generic Bokeh figure either in a browser or Jupyter Notebook, it&amp;rsquo;s time to learn more about how to configure the &lt;code&gt;figure()&lt;/code&gt; object. &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;figure()&lt;/code&gt; object is not only the foundation of your data visualization but also the object that unlocks all of Bokeh&amp;rsquo;s available tools for visualizing data. The Bokeh figure is a subclass of the &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/reference/models/plots.html#bokeh.models.plots.Plot&quot;&gt;Bokeh Plot object&lt;/a&gt;, which provides many of the parameters that make it possible to configure the aesthetic elements of your figure. &lt;/p&gt;
&lt;p&gt;To show you just a glimpse into the customization options available, let&amp;rsquo;s create the ugliest figure ever:&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;# Bokeh Libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_notebook&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# The figure will be rendered inline in my Jupyter Notebook&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_notebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Example figure&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;background_fill_color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;gray&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;background_fill_alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;border_fill_color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;blue&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;border_fill_alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt;&lt;span class=&quot;o&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;h_symmetry&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;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;X Label&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;x_axis_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;x_axis_location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;above&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;x_range&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;2018-01-01&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;2018-06-30&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Y Label&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;y_axis_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;linear&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;y_axis_location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;left&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;y_range&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;0&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;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Example Figure&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;title_location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;right&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;toolbar_location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;below&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;tools&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;c1&quot;&gt;# See what it looks like&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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/example_figure.4f94be1e1632.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/example_figure.4f94be1e1632.png&quot; width=&quot;1016&quot; height=&quot;622&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/example_figure.4f94be1e1632.png&amp;amp;w=254&amp;amp;sig=5d90408350e41cd00db16058d600a88c336ddfca 254w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/example_figure.4f94be1e1632.png&amp;amp;w=508&amp;amp;sig=4ec1cc92ccc695ae7f5cfbba52974744ac00bb89 508w, https://files.realpython.com/media/example_figure.4f94be1e1632.png 1016w&quot; sizes=&quot;75vw&quot; alt=&quot;Example Figure&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once the &lt;code&gt;figure()&lt;/code&gt; object is instantiated, you can still configure it after the fact. Let&amp;rsquo;s say you want to get rid of the gridlines:&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;# Remove the gridlines from the figure() object&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grid_line_color&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;c1&quot;&gt;# See what it looks like &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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The gridline properties are accessible via the figure&amp;rsquo;s &lt;code&gt;grid&lt;/code&gt; attribute. In this case, setting &lt;code&gt;grid_line_color&lt;/code&gt; to &lt;code&gt;None&lt;/code&gt; effectively removes the gridlines altogether. &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/reference/models/plots.html#bokeh.models.plots.Plot.axis&quot;&gt;More details about figure attributes&lt;/a&gt; can be found below the fold in the Plot class documentation. &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/example_figure_no_gridlines.b860b44b0650.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/example_figure_no_gridlines.b860b44b0650.png&quot; width=&quot;1012&quot; height=&quot;616&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/example_figure_no_gridlines.b860b44b0650.png&amp;amp;w=253&amp;amp;sig=0ce6a8ce3fb767e842b28ab2abb2fcb887a1ce37 253w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/example_figure_no_gridlines.b860b44b0650.png&amp;amp;w=506&amp;amp;sig=a752565f801613881c8b1652970b9f6a29315009 506w, https://files.realpython.com/media/example_figure_no_gridlines.b860b44b0650.png 1012w&quot; sizes=&quot;75vw&quot; alt=&quot;Example Figure w/o Gridlines&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; If you&amp;rsquo;re working in a notebook or IDE with auto-complete functionality, this feature can definitely be your friend! With so many customizable elements, it can be very helpful in discovering the available options:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/auto_complete.37c784671746.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/auto_complete.37c784671746.gif&quot; width=&quot;854&quot; height=&quot;302&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/auto_complete.37c784671746.gif&amp;amp;w=213&amp;amp;sig=ce128d62037d2cb54aad03536d2838c76cc72469 213w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/auto_complete.37c784671746.gif&amp;amp;w=427&amp;amp;sig=e92d9071f388e778a489c2543c58f17bf7a9568e 427w, https://files.realpython.com/media/auto_complete.37c784671746.gif 854w&quot; sizes=&quot;75vw&quot; alt=&quot;Auto Complete GIF&quot;/&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Otherwise, doing a quick web search, with the keyword &lt;em&gt;bokeh&lt;/em&gt; and what you are trying to do, will generally point you in the right direction. &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;There is tons more I could touch on here, but don&amp;rsquo;t feel like you&amp;rsquo;re missing out. I&amp;rsquo;ll make sure to introduce different figure tweaks as the tutorial progresses. Here are some other helpful links on the topic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/reference/models/plots.html#bokeh.models.plots.Plot&quot;&gt;The Bokeh Plot Class&lt;/a&gt;&lt;/strong&gt; is the superclass of the &lt;code&gt;figure()&lt;/code&gt; object, from which figures inherit a lot of their attributes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure&quot;&gt;The Figure Class&lt;/a&gt;&lt;/strong&gt; documentation is a good place to find more detail about the arguments of the &lt;code&gt;figure()&lt;/code&gt; object.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are a few specific customization options worth checking out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#text-properties&quot;&gt;&lt;strong&gt;Text Properties&lt;/strong&gt;&lt;/a&gt; covers all the attributes related to changing font styles, sizes, colors, and so forth.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#tick-label-formats&quot;&gt;&lt;strong&gt;TickFormatters&lt;/strong&gt;&lt;/a&gt; are built-in objects specifically for formatting your axes using Python-like string formatting syntax.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes, it isn&amp;rsquo;t clear how your figure needs to be customized until it actually has some data visualized in it, so next you&amp;rsquo;ll learn how to make that happen. &lt;/p&gt;
&lt;h2 id=&quot;drawing-data-with-glyphs&quot;&gt;Drawing Data With Glyphs&lt;/h2&gt;
&lt;p&gt;An empty figure isn&amp;rsquo;t all that exciting, so let&amp;rsquo;s look at glyphs: the building blocks of Bokeh visualizations. A glyph is a vectorized graphical shape or marker that is used to represent your data, like a circle or square. More examples can be found in the &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/gallery/markers.html&quot;&gt;Bokeh gallery&lt;/a&gt;. After you create your figure, you are given access to &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/reference/plotting.html&quot;&gt;a bevy of configurable glyph methods&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with a very basic example, drawing some points on an x-y coordinate grid:&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;# Bokeh Libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# My x-y coordinate data&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;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;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;n&quot;&gt;y&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;mi&quot;&gt;1&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;c1&quot;&gt;# Output the visualization directly in the notebook&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;first_glyphs.html&amp;#39;&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;First Glyphs&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a figure with no toolbar and axis ranges of [0,3]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&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;My Coordinates&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;x_range&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;0&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;y_range&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;0&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;toolbar_location&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;c1&quot;&gt;# Draw the coordinates as circles&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&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;x&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;n&quot;&gt;y&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;s1&quot;&gt;&amp;#39;green&amp;#39;&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;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Show plot&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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/first_glyphs.ed000f56ed12.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/first_glyphs.ed000f56ed12.png&quot; width=&quot;658&quot; height=&quot;612&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/first_glyphs.ed000f56ed12.png&amp;amp;w=164&amp;amp;sig=408e51978e7320e8bf76509284afd7a4c8e55a02 164w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/first_glyphs.ed000f56ed12.png&amp;amp;w=329&amp;amp;sig=45107f316019b02edf3a3b52b32dd0801e4be2bf 329w, https://files.realpython.com/media/first_glyphs.ed000f56ed12.png 658w&quot; sizes=&quot;75vw&quot; alt=&quot;First Glyphs&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once your figure is instantiated, you can see how it can be used to draw the x-y coordinate data using customized &lt;code&gt;circle&lt;/code&gt; glyphs.&lt;/p&gt;
&lt;p&gt;Here are a few categories of glyphs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Marker&lt;/strong&gt; includes shapes like circles, diamonds, squares, and triangles and is effective for creating visualizations like scatter and bubble charts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line&lt;/strong&gt; covers things like single, step, and multi-line shapes that can be used to build line charts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bar/Rectangle&lt;/strong&gt; shapes can be used to create traditional or stacked bar (&lt;code&gt;hbar&lt;/code&gt;) and column (&lt;code&gt;vbar&lt;/code&gt;) charts as well as waterfall or &lt;a href=&quot;https://en.wikipedia.org/wiki/Gantt_chart&quot;&gt;gantt&lt;/a&gt; charts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Information about the glyphs above, as well as others, can be found in &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/plotting.html&quot;&gt;Bokeh&amp;rsquo;s Reference Guide&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;These glyphs can be combined as needed to fit your visualization needs. Let&amp;rsquo;s say I want to create a visualization that shows how many words I wrote per day to make this tutorial, with an overlaid trend line of the cumulative word count:&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;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;np&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Bokeh libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_notebook&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# My word count data&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;day_num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linspace&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;10&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;daily_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;mi&quot;&gt;450&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;628&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;488&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;210&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;287&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;791&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;508&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;639&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;397&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;943&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cumulative_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cumsum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;daily_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output the visualization directly in the notebook&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_notebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a figure with a datetime type x-axis&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&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;My Tutorial Progress&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;plot_width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;700&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Day Number&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Words Written&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;x_minor_ticks&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;n&quot;&gt;y_range&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;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;toolbar_location&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;c1&quot;&gt;# The daily words will be represented as vertical bars (columns)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vbar&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;day_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottom&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;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;daily_words&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;s1&quot;&gt;&amp;#39;blue&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;mf&quot;&gt;0.75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
         &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Daily&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# The cumulative sum will be a trend line&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&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;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day_num&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;n&quot;&gt;cumulative_words&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;s1&quot;&gt;&amp;#39;gray&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line_width&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;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Cumulative&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Put the legend in the upper left corner&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;top_left&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Let&amp;#39;s check it out&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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/multi_glyph_example.0721ffa49fb3.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/multi_glyph_example.0721ffa49fb3.png&quot; width=&quot;1460&quot; height=&quot;822&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/multi_glyph_example.0721ffa49fb3.png&amp;amp;w=365&amp;amp;sig=8e274330d6a4d62ed6d7ed83f3c9248f5bac0621 365w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/multi_glyph_example.0721ffa49fb3.png&amp;amp;w=730&amp;amp;sig=20c9ae0b2c639f5e30e9198587e22093ffd39a04 730w, https://files.realpython.com/media/multi_glyph_example.0721ffa49fb3.png 1460w&quot; sizes=&quot;75vw&quot; alt=&quot;Multi-Glyph Example&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To combine the columns and lines on the figure, they are simply created using the same &lt;code&gt;figure()&lt;/code&gt; object. &lt;/p&gt;
&lt;p&gt;Additionally, you can see above how seamlessly a legend can be created by setting the &lt;code&gt;legend&lt;/code&gt; property for each glyph. The legend was then moved to the upper left corner of the plot by assigning &lt;code&gt;&#39;top_left&#39;&lt;/code&gt; to &lt;code&gt;fig.legend.location&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;You can check out &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#legends&quot;&gt;much more info about styling legends&lt;/a&gt;. Teaser: they will show up again later in the tutorial when we start digging into interactive elements of the visualization. &lt;/p&gt;
&lt;h2 id=&quot;a-quick-aside-about-data&quot;&gt;A Quick Aside About Data&lt;/h2&gt;
&lt;p&gt;Anytime you are exploring a new visualization library, it&amp;rsquo;s a good idea to start with some data in a domain you are familiar with. The beauty of Bokeh is that nearly any idea you have should be possible. It&amp;rsquo;s just a matter of how you want to leverage the available tools to do so. &lt;/p&gt;
&lt;p&gt;The remaining examples will use publicly available data from Kaggle, which has information about &lt;a href=&quot;https://www.kaggle.com/pablote/nba-enhanced-stats&quot;&gt;the National Basketball Association&amp;rsquo;s (NBA) 2017-18 season&lt;/a&gt;, specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kaggle.com/pablote/nba-enhanced-stats#2017-18_playerBoxScore.csv&quot;&gt;&lt;strong&gt;2017-18_playerBoxScore.csv&lt;/strong&gt;&lt;/a&gt;: game-by-game snapshots of player statistics&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kaggle.com/pablote/nba-enhanced-stats#2017-18_teamBoxScore.csv&quot;&gt;&lt;strong&gt;2017-18_teamBoxScore.csv&lt;/strong&gt;&lt;/a&gt;: game-by-game snapshots of team statistics&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kaggle.com/pablote/nba-enhanced-stats#2017-18_standings.csv&quot;&gt;&lt;strong&gt;2017-18_standings.csv&lt;/strong&gt;&lt;/a&gt;: daily team standings and rankings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This data has nothing to do with what I do for work, but I love basketball and enjoy thinking about ways to visualize the ever-growing amount of data associated with it. &lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t have data to play with from school or work, think about something you&amp;rsquo;re interested in and try to find some data related to that. It will go a long way in making both the learning and the creative process faster and more enjoyable! &lt;/p&gt;
&lt;p&gt;To follow along with the examples in the tutorial, you can download the datasets from the links above and read them into a  Pandas &lt;code&gt;DataFrame&lt;/code&gt; using the following commands:&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;pandas&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pd&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Read the csv files&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;player_stats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;2017-18_playerBoxScore.csv&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse_dates&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;gmDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;team_stats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;2017-18_teamBoxScore.csv&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse_dates&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;gmDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;standings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;2017-18_standings.csv&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse_dates&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;stDate&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 code snippet reads the data from the three CSV files and automatically interprets the date columns as &lt;a href=&quot;https://docs.python.org/3/library/datetime.html&quot;&gt;&lt;code&gt;datetime&lt;/code&gt; objects&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s now time to get your hands on some real data. &lt;/p&gt;
&lt;h2 id=&quot;using-the-columndatasource-object&quot;&gt;Using the &lt;code&gt;ColumnDataSource&lt;/code&gt; Object&lt;/h2&gt;
&lt;p&gt;The examples above used &lt;a href=&quot;https://realpython.com/python-lists-tuples/#python-lists&quot;&gt;Python lists&lt;/a&gt; and &lt;a href=&quot;https://realpython.com/numpy-array-programming/&quot;&gt;Numpy arrays&lt;/a&gt; to represent the data, and Bokeh is well equipped to handle these datatypes. However, when it comes to data in Python, you are most likely going to come across &lt;a href=&quot;https://realpython.com/python-dicts/&quot;&gt;Python dictionaries&lt;/a&gt; and &lt;a href=&quot;http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html#pandas.DataFrame&quot;&gt;Pandas DataFrames&lt;/a&gt;, especially if you&amp;rsquo;re reading in data from a file or external data source. &lt;/p&gt;
&lt;p&gt;Bokeh is well equipped to work with these more complex data structures and even has built-in functionality to handle them, namely the &lt;code&gt;ColumnDataSource&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You may be asking yourself, &amp;ldquo;Why use a &lt;code&gt;ColumnDataSource&lt;/code&gt; when Bokeh can interface with other data types directly?&amp;rdquo; &lt;/p&gt;
&lt;p&gt;For one, whether you reference a list, array, dictionary, or DataFrame directly, Bokeh is going to turn it into a &lt;code&gt;ColumnDataSource&lt;/code&gt; behind the scenes anyway. More importantly, the &lt;code&gt;ColumnDataSource&lt;/code&gt; makes it much easier to implement Bokeh&amp;rsquo;s interactive affordances. &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ColumnDataSource&lt;/code&gt; is foundational in passing the data to the glyphs you are using to visualize. Its primary functionality is to map names to the columns of your data. This makes it easier for you to reference elements of your data when building your visualization. It also makes it easier for Bokeh to do the same when building your visualization. &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ColumnDataSource&lt;/code&gt; can interpret three types of data objects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Python &lt;code&gt;dict&lt;/code&gt;&lt;/strong&gt;: The keys are names associated with the respective value sequences (lists, arrays, and so forth).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pandas &lt;code&gt;DataFrame&lt;/code&gt;&lt;/strong&gt;: The columns of the &lt;code&gt;DataFrame&lt;/code&gt; become the reference names for the &lt;code&gt;ColumnDataSource&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pandas &lt;code&gt;groupby&lt;/code&gt;&lt;/strong&gt;: The columns of the &lt;code&gt;ColumnDataSource&lt;/code&gt; reference the columns as seen by calling &lt;code&gt;groupby.describe()&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s start by visualizing the race for first place in the NBA&amp;rsquo;s Western Conference in 2017-18 between the defending champion Golden State Warriors and the challenger Houston Rockets. The daily win-loss records of these two teams is stored in a DataFrame named &lt;code&gt;west_top_2&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;west_top_2&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;standings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&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;HOU&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&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;GS&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&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;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&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;west_top_2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;        stDate teamAbbr  gameWon&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;9   2017-10-17       GS        0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;39  2017-10-18       GS        0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;69  2017-10-19       GS        0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;99  2017-10-20       GS        1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;129 2017-10-21       GS        1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;From here, you can load this &lt;code&gt;DataFrame&lt;/code&gt; into two &lt;code&gt;ColumnDataSource&lt;/code&gt; objects and visualize the race:&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;# Bokeh libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output to file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;west-top-2-standings-race.html&amp;#39;&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;Western Conference Top 2 Teams Wins Race&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Isolate the data for the Rockets and Warriors&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rockets_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;west_top_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west_top_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&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;HOU&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;warriors_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;west_top_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west_top_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&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;GS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a ColumnDataSource object for each team&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rockets_cds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rockets_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;warriors_cds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warriors_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create and configure the figure&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_axis_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;600&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;Western Conference Top 2 Teams Wins Race, 2017-18&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Date&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Wins&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;toolbar_location&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;c1&quot;&gt;# Render the race as step lines&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;s1&quot;&gt;&amp;#39;#CE1141&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Rockets&amp;#39;&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;rockets_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;s1&quot;&gt;&amp;#39;#006BB6&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Warriors&amp;#39;&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;warriors_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Move the legend to the upper left corner&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;top_left&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Show the plot&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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/rockets_v_warriors.9d4eda72977b.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/rockets_v_warriors.9d4eda72977b.png&quot; width=&quot;1214&quot; height=&quot;598&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/rockets_v_warriors.9d4eda72977b.png&amp;amp;w=303&amp;amp;sig=632025f9d1a2ca29fcf693f167126ae0d99ab7a0 303w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/rockets_v_warriors.9d4eda72977b.png&amp;amp;w=607&amp;amp;sig=16f6ba63dde1eade261e7d7cc9674250af0fa2e3 607w, https://files.realpython.com/media/rockets_v_warriors.9d4eda72977b.png 1214w&quot; sizes=&quot;75vw&quot; alt=&quot;Rockets vs. Warriors&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Notice how the respective &lt;code&gt;ColumnDataSource&lt;/code&gt; objects are referenced when creating the two lines. You simply pass the original column names as input parameters and specify which &lt;code&gt;ColumnDataSource&lt;/code&gt; to use via the &lt;code&gt;source&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;The visualization shows the tight race throughout the season, with the Warriors building a pretty big cushion around the middle of the season. However, a bit of a late-season slide allowed the Rockets to catch up and ultimately surpass the defending champs to finish the season as the Western Conference number-one seed.&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; In Bokeh, you can specify colors either by name, hex value, or RGB color code. &lt;/p&gt;
&lt;p&gt;For the visualization above, a color is being specified for the respective lines representing the two teams. Instead of using CSS color names like &lt;code&gt;&#39;red&#39;&lt;/code&gt; for the Rockets and &lt;code&gt;&#39;blue&#39;&lt;/code&gt; for the Warriors, you might have wanted to add a nice visual touch by using the &lt;a href=&quot;https://teamcolorcodes.com/nba-team-color-codes/&quot;&gt;official team colors&lt;/a&gt; in the form of hex color codes. Alternatively, you could have used tuples representing RGB color codes: &lt;code&gt;(206, 17, 65)&lt;/code&gt; for the Rockets, &lt;code&gt;(0, 107, 182)&lt;/code&gt; for the Warriors.&lt;/p&gt;
&lt;p&gt;Bokeh provides a helpful &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/reference/colors.html#bokeh-colors-groups&quot;&gt;list of CSS color names categorized by their general hue&lt;/a&gt;. Also, &lt;a href=&quot;https://htmlcolorcodes.com&quot;&gt;htmlcolorcodes.com&lt;/a&gt; is a great site for finding CSS, hex, and RGB color codes.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;ColumnDataSource&lt;/code&gt; objects can do more than just serve as an easy way to reference &lt;code&gt;DataFrame&lt;/code&gt; columns. The &lt;code&gt;ColumnDataSource&lt;/code&gt; object has three built-in filters that can be used to create views on your data using a &lt;code&gt;CDSView&lt;/code&gt; object:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;GroupFilter&lt;/code&gt;&lt;/strong&gt; selects rows from a &lt;code&gt;ColumnDataSource&lt;/code&gt; based on a categorical reference value&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;IndexFilter&lt;/code&gt;&lt;/strong&gt; filters the &lt;code&gt;ColumnDataSource&lt;/code&gt; via a list of integer indices&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;BooleanFilter&lt;/code&gt;&lt;/strong&gt; allows you to use a list of &lt;code&gt;boolean&lt;/code&gt; values, with &lt;code&gt;True&lt;/code&gt; rows being selected&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the previous example, two &lt;code&gt;ColumnDataSource&lt;/code&gt; objects were created, one each from a subset of the &lt;code&gt;west_top_2&lt;/code&gt; DataFrame. The next example will recreate the same output from one &lt;code&gt;ColumnDataSource&lt;/code&gt; based on all of &lt;code&gt;west_top_2&lt;/code&gt; using a &lt;code&gt;GroupFilter&lt;/code&gt; that creates a view on the data:&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;# Bokeh libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GroupFilter&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output to file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;west-top-2-standings-race.html&amp;#39;&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;Western Conference Top 2 Teams Wins Race&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a ColumnDataSource&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_cds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west_top_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create views for each team&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rockets_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;west_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;n&quot;&gt;filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;HOU&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;warriors_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;west_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;GS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create and configure the figure&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_axis_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;600&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;Western Conference Top 2 Teams Wins Race, 2017-18&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Date&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Wins&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;toolbar_location&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;c1&quot;&gt;# Render the race as step lines&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;west_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rockets_view&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;s1&quot;&gt;&amp;#39;#CE1141&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Rockets&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;west_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warriors_view&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;s1&quot;&gt;&amp;#39;#006BB6&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Warriors&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Move the legend to the upper left corner&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;top_left&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Show the plot&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;west_fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/rockets_v_warriors_v2.8b33de43b78c.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/rockets_v_warriors_v2.8b33de43b78c.png&quot; width=&quot;1230&quot; height=&quot;608&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/rockets_v_warriors_v2.8b33de43b78c.png&amp;amp;w=307&amp;amp;sig=aa3e354ff064d9ce6295783bf13bd614cf63b58e 307w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/rockets_v_warriors_v2.8b33de43b78c.png&amp;amp;w=615&amp;amp;sig=46d04baf2df42a5a7ca6c5e977604c42c21eadf0 615w, https://files.realpython.com/media/rockets_v_warriors_v2.8b33de43b78c.png 1230w&quot; sizes=&quot;75vw&quot; alt=&quot;Rockets vs. Warriors 2&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Notice how the &lt;code&gt;GroupFilter&lt;/code&gt; is passed to &lt;code&gt;CDSView&lt;/code&gt; in a list. This allows you to combine multiple filters together to isolate the data you need from the &lt;code&gt;ColumnDataSource&lt;/code&gt; as needed. &lt;/p&gt;
&lt;p&gt;For information about integrating data sources, check out the Bokeh user guide&amp;rsquo;s post on the  &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/data.html&quot;&gt;&lt;code&gt;ColumnDataSource&lt;/code&gt; and other source objects available&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Western Conference ended up being an exciting race, but say you want to see if the Eastern Conference was just as tight. Not only that, but you&amp;rsquo;d  like to view them in a single visualization. This is a perfect segue to the next topic: layouts.&lt;/p&gt;
&lt;h2 id=&quot;organizing-multiple-visualizations-with-layouts&quot;&gt;Organizing Multiple Visualizations With Layouts&lt;/h2&gt;
&lt;p&gt;The Eastern Conference standings came down to two rivals in the Atlantic Division: the Boston Celtics and the Toronto Raptors. Before replicating the steps used to create &lt;code&gt;west_top_2&lt;/code&gt;, let&amp;rsquo;s try to put the &lt;code&gt;ColumnDataSource&lt;/code&gt; to the test one more time using what you learned above. &lt;/p&gt;
&lt;p&gt;In this example, you&amp;rsquo;ll see how to feed an entire DataFrame into a &lt;code&gt;ColumnDataSource&lt;/code&gt; and create views to isolate the relevant data:&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;# Bokeh libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GroupFilter&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output to file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;east-top-2-standings-race.html&amp;#39;&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;Eastern Conference Top 2 Teams Wins Race&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a ColumnDataSource&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;standings_cds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create views for each team&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;celtics_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                           &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;BOS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;raptors_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                           &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;TOR&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create and configure the figure&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_axis_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;600&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;Eastern Conference Top 2 Teams Wins Race, 2017-18&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Date&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Wins&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;toolbar_location&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;c1&quot;&gt;# Render the race as step lines&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;s1&quot;&gt;&amp;#39;#007A33&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Celtics&amp;#39;&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;celtics_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;s1&quot;&gt;&amp;#39;#CE1141&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Raptors&amp;#39;&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raptors_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Move the legend to the upper left corner&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;top_left&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Show the plot&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;east_fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/celtics_v_raptors.86506516532b.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/celtics_v_raptors.86506516532b.png&quot; width=&quot;1234&quot; height=&quot;614&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/celtics_v_raptors.86506516532b.png&amp;amp;w=308&amp;amp;sig=0b25b106cab3b4644089ab16c8efe1263f06a79b 308w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/celtics_v_raptors.86506516532b.png&amp;amp;w=617&amp;amp;sig=e085b8c819e25f57dd8156120bf1584c10f2c048 617w, https://files.realpython.com/media/celtics_v_raptors.86506516532b.png 1234w&quot; sizes=&quot;75vw&quot; alt=&quot;Celtics vs. Raptors&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ColumnDataSource&lt;/code&gt; was able to isolate the relevant data within a 5,040-by-39 &lt;code&gt;DataFrame&lt;/code&gt; without breaking a sweat, saving a few lines of Pandas code in the process. &lt;/p&gt;
&lt;p&gt;Looking at the visualization, you can see that the Eastern Conference race was no slouch. After the Celtics roared out of the gate, the Raptors clawed all the way back to overtake their division rival and finish the regular season with five more wins. &lt;/p&gt;
&lt;p&gt;With our two visualizations ready, it&amp;rsquo;s time to put them together.&lt;/p&gt;
&lt;p&gt;Similar to the functionality of &lt;a href=&quot;https://realpython.com/python-matplotlib-guide/#understanding-pltsubplots-notation&quot;&gt;Matplotlib&amp;rsquo;s &lt;code&gt;subplot&lt;/code&gt;&lt;/a&gt;, Bokeh offers the &lt;code&gt;column&lt;/code&gt;, &lt;code&gt;row&lt;/code&gt;, and &lt;code&gt;gridplot&lt;/code&gt; functions in its &lt;code&gt;bokeh.layouts&lt;/code&gt; module. These functions can more generally be classified as &lt;strong&gt;layouts&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The usage is very straightforward. If you want to put two visualizations in a vertical configuration, you can do so with 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;c1&quot;&gt;# Bokeh library&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.layouts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output to file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;east-west-top-2-standings-race.html&amp;#39;&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;Conference Top 2 Teams Wins Race&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Plot the two visualizations in a vertical configuration&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;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/column_layout.db1f023f726b.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/column_layout.db1f023f726b.png&quot; width=&quot;1240&quot; height=&quot;1240&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/column_layout.db1f023f726b.png&amp;amp;w=310&amp;amp;sig=4a6a34adc7113709b5dc8264e87d06fa46a7caa6 310w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/column_layout.db1f023f726b.png&amp;amp;w=620&amp;amp;sig=2d5b5b147c7a520bd6188ffe4e395bb9d263972e 620w, https://files.realpython.com/media/column_layout.db1f023f726b.png 1240w&quot; sizes=&quot;75vw&quot; alt=&quot;Column Layout&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll save you the two lines of code, but rest assured that swapping &lt;code&gt;column&lt;/code&gt; for &lt;code&gt;row&lt;/code&gt; in the snippet above will similarly configure the two plots in a horizontal configuration.&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&amp;rsquo;re trying out the code snippets as you go through the tutorial, I want to take a quick detour to address an error you may see accessing &lt;code&gt;west_fig&lt;/code&gt; and  &lt;code&gt;east_fig&lt;/code&gt; in the following examples. In doing so, you may receive an error 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:bokeh.core.validation.check:W-1004 (BOTH_CHILD_AND_ROOT): Models should not be a document root...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is one of many errors that are part of Bokeh&amp;rsquo;s &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/reference/core/validation.html&quot;&gt;validation module&lt;/a&gt;, where &lt;code&gt;w-1004&lt;/code&gt; in particular is warning about the re-use of  &lt;code&gt;west_fig&lt;/code&gt; and &lt;code&gt;east_fig&lt;/code&gt; in a new layout. &lt;/p&gt;
&lt;p&gt;To avoid this error as you test the examples, preface the code snippet illustrating each layout with 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;c1&quot;&gt;# Bokeh libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GroupFilter&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a ColumnDataSource&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;standings_cds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create the views for each team&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;celtics_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                           &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;BOS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;raptors_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                           &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;TOR&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;rockets_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                           &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;HOU&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;warriors_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                           &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;GS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create and configure the figure&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_axis_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Date&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Wins&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;toolbar_location&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;west_fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_axis_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Date&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Wins&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;toolbar_location&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;c1&quot;&gt;# Configure the figures for each conference&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;s1&quot;&gt;&amp;#39;#007A33&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Celtics&amp;#39;&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;celtics_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;s1&quot;&gt;&amp;#39;#CE1141&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Raptors&amp;#39;&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raptors_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;s1&quot;&gt;&amp;#39;#CE1141&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Rockets&amp;#39;&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rockets_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;stDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gameWon&amp;#39;&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;s1&quot;&gt;&amp;#39;#006BB6&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Warriors&amp;#39;&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;standings_cds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warriors_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Move the legend to the upper left corner&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;top_left&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;top_left&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Layout code snippet goes here!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Doing so will renew the relevant components to render the visualization, ensuring that no warning is needed.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Instead of using &lt;code&gt;column&lt;/code&gt; or &lt;code&gt;row&lt;/code&gt;, you may want to use a &lt;code&gt;gridplot&lt;/code&gt; instead. &lt;/p&gt;
&lt;p&gt;One key difference of &lt;code&gt;gridplot&lt;/code&gt; is that it will automatically consolidate the toolbar across all of its children figures. The two visualizations above do not have a toolbar, but if they did, then  each figure would have its own when using &lt;code&gt;column&lt;/code&gt; or &lt;code&gt;row&lt;/code&gt;.  With that, it also has its own &lt;code&gt;toolbar_location&lt;/code&gt; property, seen below set to &lt;code&gt;&#39;right&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Syntactically, you&amp;rsquo;ll also notice below that &lt;code&gt;gridplot&lt;/code&gt; differs in that, instead of being passed a tuple as input, it requires a list of lists, where each sub-list represents a row in the grid:&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;# Bokeh libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.layouts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gridplot&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output to file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;east-west-top-2-gridplot.html&amp;#39;&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;Conference Top 2 Teams Wins Race&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Reduce the width of both figures&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Edit the titles&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&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;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eastern Conference&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_fig&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;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Western Conference&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Configure the gridplot&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_west_gridplot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gridplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt; 
                              &lt;span class=&quot;n&quot;&gt;toolbar_location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;right&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Plot the two visualizations in a horizontal configuration&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;east_west_gridplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/gridplot_layout.c7eb7b37803d.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/gridplot_layout.c7eb7b37803d.png&quot; width=&quot;1290&quot; height=&quot;642&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gridplot_layout.c7eb7b37803d.png&amp;amp;w=322&amp;amp;sig=577265911375bca2a017bc7d80e82c655d50bac2 322w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gridplot_layout.c7eb7b37803d.png&amp;amp;w=645&amp;amp;sig=0e06e577ab7f78237cebffedb0bd835120225c24 645w, https://files.realpython.com/media/gridplot_layout.c7eb7b37803d.png 1290w&quot; sizes=&quot;75vw&quot; alt=&quot;Gridplot Layout&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Lastly, &lt;code&gt;gridplot&lt;/code&gt; allows the passing of &lt;code&gt;None&lt;/code&gt; values, which are interpreted as blank subplots. Therefore, if you wanted to leave a placeholder for two additional plots, then you could do 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;c1&quot;&gt;# Bokeh libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.layouts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gridplot&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output to file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;east-west-top-2-gridplot.html&amp;#39;&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;Conference Top 2 Teams Wins Race&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Reduce the width of both figures&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Edit the titles&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&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;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eastern Conference&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_fig&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;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Western Conference&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Plot the two visualizations with placeholders&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_west_gridplot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gridplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;p&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;p&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;east_fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt; 
                              &lt;span class=&quot;n&quot;&gt;toolbar_location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;right&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Plot the two visualizations in a horizontal configuration&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;east_west_gridplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/gridplot_with_nones.cc7feb66a672.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/gridplot_with_nones.cc7feb66a672.png&quot; width=&quot;1290&quot; height=&quot;1218&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gridplot_with_nones.cc7feb66a672.png&amp;amp;w=322&amp;amp;sig=de0bc38732951f57159a1cd540f6d039acdb0542 322w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gridplot_with_nones.cc7feb66a672.png&amp;amp;w=645&amp;amp;sig=aaa9d0d57d270394a1a4a3a8b78b7f116d038662 645w, https://files.realpython.com/media/gridplot_with_nones.cc7feb66a672.png 1290w&quot; sizes=&quot;75vw&quot; alt=&quot;Gridplot with Nones&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d rather toggle between both visualizations at their full size without having to squash them down to fit next to or on top of each other, a good option is a tabbed layout.&lt;/p&gt;
&lt;p&gt;A tabbed layout consists of two Bokeh widget functions: &lt;code&gt;Tab()&lt;/code&gt; and &lt;code&gt;Panel()&lt;/code&gt; from the &lt;code&gt;bokeh.models.widgets&lt;/code&gt; sub-module. Like using &lt;code&gt;gridplot()&lt;/code&gt;, making a tabbed layout is pretty straightforward:&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;# Bokeh Library&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models.widgets&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tabs&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;c1&quot;&gt;# Output to file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;east-west-top-2-tabbed_layout.html&amp;#39;&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;Conference Top 2 Teams Wins Race&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Increase the plot widths&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;west_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;800&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create two panels, one for each conference&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;east_panel&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;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;east_fig&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;Eastern Conference&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;west_panel&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;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west_fig&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;Western Conference&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Assign the panels to Tabs&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tabs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tabs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tabs&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;west_panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;east_panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Show the tabbed layout&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;tabs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/tabbed_layout.2566a4615fcf.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/tabbed_layout.2566a4615fcf.gif&quot; width=&quot;822&quot; height=&quot;354&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/tabbed_layout.2566a4615fcf.gif&amp;amp;w=205&amp;amp;sig=09a3a48fb9293e908024de1af6e036190b9245ad 205w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/tabbed_layout.2566a4615fcf.gif&amp;amp;w=411&amp;amp;sig=050739cbe326ac19a9637853e7a14e813807d075 411w, https://files.realpython.com/media/tabbed_layout.2566a4615fcf.gif 822w&quot; sizes=&quot;75vw&quot; alt=&quot;Tabbed Layout GIF&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The first step is to create a &lt;code&gt;Panel()&lt;/code&gt; for each tab. That may sound a little confusing, but think of the &lt;code&gt;Tabs()&lt;/code&gt; function as the mechanism that organizes the individual tabs created with &lt;code&gt;Panel()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Each &lt;code&gt;Panel()&lt;/code&gt; takes as input a child, which can either be a single &lt;code&gt;figure()&lt;/code&gt; or a layout. (Remember that a layout is a general name for a &lt;code&gt;column&lt;/code&gt;, &lt;code&gt;row&lt;/code&gt;, or &lt;code&gt;gridplot&lt;/code&gt;.) Once your panels are assembled, they can be passed as input to &lt;code&gt;Tabs()&lt;/code&gt; in a list.&lt;/p&gt;
&lt;p&gt;Now that you understand how to access, draw, and organize your data, it&amp;rsquo;s time to move on to the real magic of Bokeh: interaction! As always, check out Bokeh&amp;rsquo;s User Guide for more information on &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/layout.html&quot;&gt;layouts&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;adding-interaction&quot;&gt;Adding Interaction&lt;/h2&gt;
&lt;p&gt;The feature that sets Bokeh apart is its ability to easily implement interactivity in your visualization. Bokeh even goes as far as describing itself as an &lt;strong&gt;interactive visualization library&lt;/strong&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bokeh is an interactive visualization library that targets modern web browsers for presentation. (&lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/quickstart.html#userguide-quickstart&quot;&gt;Source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this section, we&amp;rsquo;ll touch on five ways that you can add interactivity:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configuring the toolbar&lt;/li&gt;
&lt;li&gt;Selecting data points&lt;/li&gt;
&lt;li&gt;Adding hover actions&lt;/li&gt;
&lt;li&gt;Linking axes and selections&lt;/li&gt;
&lt;li&gt;Highlighting data using the legend&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Implementing these interactive elements open up possibilities for exploring your data that static visualizations just can&amp;rsquo;t do by themselves.  &lt;/p&gt;
&lt;h3 id=&quot;configuring-the-toolbar&quot;&gt;Configuring the Toolbar&lt;/h3&gt;
&lt;p&gt;As you saw all the way back in &lt;a href=&quot;#generating-your-first-figure&quot;&gt;Generating Your First Figure&lt;/a&gt;, the default Bokeh &lt;code&gt;figure()&lt;/code&gt; comes with a toolbar right out of the box. The default toolbar comes with the following tools (from left to right):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pan&lt;/li&gt;
&lt;li&gt;Box Zoom&lt;/li&gt;
&lt;li&gt;Wheel Zoom&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;li&gt;Reset&lt;/li&gt;
&lt;li&gt;A link to &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/tools.html#built-in-tools&quot;&gt;&lt;strong&gt;Bokeh&amp;rsquo;s user guide for Configuring Plot Tools&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A link to the &lt;a href=&quot;https://bokeh.pydata.org/en/latest/&quot;&gt;&lt;strong&gt;Bokeh homepage&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The toolbar can be removed by passing &lt;code&gt;toolbar_location=None&lt;/code&gt; when instantiating a &lt;code&gt;figure()&lt;/code&gt; object, or relocated by passing any of &lt;code&gt;&#39;above&#39;&lt;/code&gt;, &lt;code&gt;&#39;below&#39;&lt;/code&gt;, &lt;code&gt;&#39;left&#39;&lt;/code&gt;, or &lt;code&gt;&#39;right&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, the toolbar can be configured to include any combination of tools you desire. Bokeh offers 18 specific tools across five categories: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pan/Drag&lt;/strong&gt;: &lt;code&gt;box_select&lt;/code&gt;, &lt;code&gt;box_zoom&lt;/code&gt;, &lt;code&gt;lasso_select&lt;/code&gt;, &lt;code&gt;pan&lt;/code&gt;, &lt;code&gt;xpan&lt;/code&gt;, &lt;code&gt;ypan&lt;/code&gt;, &lt;code&gt;resize_select&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Click/Tap&lt;/strong&gt;: &lt;code&gt;poly_select&lt;/code&gt;, &lt;code&gt;tap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scroll/Pinch&lt;/strong&gt;: &lt;code&gt;wheel_zoom&lt;/code&gt;, &lt;code&gt;xwheel_zoom&lt;/code&gt;, &lt;code&gt;ywheel_zoom&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Actions&lt;/strong&gt;: &lt;code&gt;undo&lt;/code&gt;, &lt;code&gt;redo&lt;/code&gt;, &lt;code&gt;reset&lt;/code&gt;, &lt;code&gt;save&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inspectors&lt;/strong&gt;: &lt;code&gt;crosshair&lt;/code&gt;, &lt;code&gt;hover&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To geek out on tools , make sure to visit &lt;a href=&quot;http://bokeh.pydata.org/en/0.11.1/docs/user_guide/tools.html#specifying-tools&quot;&gt;Specifying Tools&lt;/a&gt;. Otherwise, they&amp;rsquo;ll be illustrated in covering the various interactions covered herein.&lt;/p&gt;
&lt;h3 id=&quot;selecting-data-points&quot;&gt;Selecting Data Points&lt;/h3&gt;
&lt;p&gt;Implementing selection behavior is as easy as adding a few specific keywords when declaring your glyphs. &lt;/p&gt;
&lt;p&gt;The next example will create a scatter plot that relates a player&amp;rsquo;s total number of three-point shot attempts to the percentage made (for players with at least 100 three-point shot attempts). &lt;/p&gt;
&lt;p&gt;The data can be aggregated from the &lt;code&gt;player_stats&lt;/code&gt; DataFrame:&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;# Find players who took at least 1 three-point shot during the season&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;three_takers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;play3PA&amp;#39;&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;c1&quot;&gt;# Clean up the player names, placing them in a single column&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;three_takers&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;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;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{p[&amp;quot;playFNm&amp;quot;]}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{p[&amp;quot;playLNm&amp;quot;]}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;three_takers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterrows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Aggregate the total three-point attempts and makes for each player&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;three_takers&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;three_takers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;groupby&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;n&quot;&gt;sum&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;loc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;play3PA&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;play3PM&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;sort_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;play3PA&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ascending&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;c1&quot;&gt;# Filter out anyone who didn&amp;#39;t take at least 100 three-point shots&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;three_takers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;three_takers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;three_takers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;play3PA&amp;#39;&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;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;n&quot;&gt;reset_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add a column with a calculated three-point percentage (made/attempted)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;three_takers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;pct3PM&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;three_takers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;play3PM&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;three_takers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;play3PA&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s a sample of the resulting &lt;code&gt;DataFrame&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;three_takers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&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;go&quot;&gt;                   name  play3PA  play3PM    pct3PM&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;229        Corey Brewer      110       31  0.281818&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;78           Marc Gasol      320      109  0.340625&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;126      Raymond Felton      230       81  0.352174&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;127  Kristaps Porziņģis      229       90  0.393013&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;66      Josh Richardson      336      127  0.377976&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let&amp;rsquo;s say you want to select a groups of players in the distribution, and in doing so mute the color of the glyphs representing the non-selected players:&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;# Bokeh Libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NumeralTickFormatter&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output to file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;three-point-att-vs-pct.html&amp;#39;&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;Three-Point Attempts vs. Percentage&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Store the data in a ColumnDataSource&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;three_takers_cds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;three_takers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Specify the selection tools to be made available&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;select_tools&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;box_select&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;lasso_select&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;poly_select&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;tap&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;reset&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create the figure&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;plot_width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Three-Point Shots Attempted&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Percentage Made&amp;#39;&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;3PT Shots Attempted vs. Percentage Made (min. 100 3PA), 2017-18&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;toolbar_location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;below&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;select_tools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Format the y-axis tick labels as percentages&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaxis&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NumeralTickFormatter&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;s1&quot;&gt;&amp;#39;00.0%&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add square representing each player&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&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;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;play3PA&amp;#39;&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;s1&quot;&gt;&amp;#39;pct3PM&amp;#39;&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;three_takers_cds&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;s1&quot;&gt;&amp;#39;royalblue&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;selection_color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;deepskyblue&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;nonselection_color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;lightgray&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;nonselection_alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Visualize&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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, specify the selection tools you want to make available. In the example above,  &lt;code&gt;&#39;box_select&#39;&lt;/code&gt;, &lt;code&gt;&#39;lasso_select&#39;&lt;/code&gt;, &lt;code&gt;&#39;poly_select&#39;&lt;/code&gt;, and &lt;code&gt;&#39;tap&#39;&lt;/code&gt; (plus a reset button) were specified in a list called &lt;code&gt;select_tools&lt;/code&gt;. When the figure is instantiated, the toolbar is positioned &lt;code&gt;&#39;below&#39;&lt;/code&gt; the plot, and the list is passed to &lt;code&gt;tools&lt;/code&gt; to make the tools selected above available.&lt;/p&gt;
&lt;p&gt;Each player is initially represented by a royal blue square glyph, but the following configurations are set for when a player or group of players is selected:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Turn the selected player(s) to &lt;code&gt;deepskyblue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Change all non-selected players&amp;rsquo; glyphs to a &lt;code&gt;lightgray&lt;/code&gt; color with &lt;code&gt;0.3&lt;/code&gt; opacity &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s it! With just a few quick additions, the visualization now looks like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/selection_example.d9ac1d0c8987.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/selection_example.d9ac1d0c8987.gif&quot; width=&quot;628&quot; height=&quot;402&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/selection_example.d9ac1d0c8987.gif&amp;amp;w=157&amp;amp;sig=45a12e3f806023b39fc16363b2965cca880055b6 157w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/selection_example.d9ac1d0c8987.gif&amp;amp;w=314&amp;amp;sig=bd392edbd47121819860dc95a4b5bd4f2651aadc 314w, https://files.realpython.com/media/selection_example.d9ac1d0c8987.gif 628w&quot; sizes=&quot;75vw&quot; alt=&quot;Selection Example GIF&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For even more information about what you can do upon selection, check out &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#selected-and-unselected-glyphs&quot;&gt;Selected and Unselected Glyphs&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;adding-hover-actions&quot;&gt;Adding Hover Actions&lt;/h3&gt;
&lt;p&gt;So the ability to select specific player data points that seem of interest in my scatter plot is implemented, but what if you want to quickly see what individual players a glyph represents? One option is to use Bokeh&amp;rsquo;s &lt;code&gt;HoverTool()&lt;/code&gt; to show a tooltip when the cursor crosses paths with a glyph. All you need to do is append the following to the code snippet above:&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;# Bokeh Library&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HoverTool&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Format the tooltip&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tooltips&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Player&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Three-Pointers Made&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;@play3PM&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;s1&quot;&gt;&amp;#39;Three-Pointers Attempted&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;@play3PA&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;s1&quot;&gt;&amp;#39;Three-Point Percentage&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;@pct3PM{00.0%}&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;# Add the HoverTool to the figure&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_tools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HoverTool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tooltips&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tooltips&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Visualize&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;fig&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;HoverTool()&lt;/code&gt; is slightly different than the selection tools you saw above in that it has properties, specifically &lt;code&gt;tooltips&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First, you can configure a formatted tooltip by creating a list of tuples containing a description and reference to the &lt;code&gt;ColumnDataSource&lt;/code&gt;. This list was passed as input to the &lt;code&gt;HoverTool()&lt;/code&gt; and then simply added to the figure using  &lt;code&gt;add_tools()&lt;/code&gt;. Here&amp;rsquo;s what happened:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/hover_tooltip_example.ff00e6668f0b.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/hover_tooltip_example.ff00e6668f0b.gif&quot; width=&quot;628&quot; height=&quot;400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hover_tooltip_example.ff00e6668f0b.gif&amp;amp;w=157&amp;amp;sig=44ea402b5cccba0ac011d3e2d97cc2b80779db63 157w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hover_tooltip_example.ff00e6668f0b.gif&amp;amp;w=314&amp;amp;sig=ca8bb84a91008772d34c10aa02d0da64ef982a46 314w, https://files.realpython.com/media/hover_tooltip_example.ff00e6668f0b.gif 628w&quot; sizes=&quot;75vw&quot; alt=&quot;Hover Tooltip Example GIF&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Notice the addition of the &lt;em&gt;Hover&lt;/em&gt; button to the toolbar, which can be toggled on and off.&lt;/p&gt;
&lt;p&gt;If you want to even further emphasize the players on hover, Bokeh makes that possible with hover inspections. Here is a slightly modified version of the code snippet that added the tooltip:&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;# Format the tooltip&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tooltips&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Player&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Three-Pointers Made&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;@play3PM&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;s1&quot;&gt;&amp;#39;Three-Pointers Attempted&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;@play3PA&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;s1&quot;&gt;&amp;#39;Three-Point Percentage&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;@pct3PM{00.0%}&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;# Configure a renderer to be used upon hover&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;hover_glyph&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&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;s1&quot;&gt;&amp;#39;play3PA&amp;#39;&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;s1&quot;&gt;&amp;#39;pct3PM&amp;#39;&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;three_takers_cds&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;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&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;n&quot;&gt;hover_fill_color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hover_alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add the HoverTool to the figure&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_tools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HoverTool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tooltips&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tooltips&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderers&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;hover_glyph&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Visualize&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;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is done by creating a completely new glyph, in this case circles instead of squares, and assigning it to &lt;code&gt;hover_glyph&lt;/code&gt;. Note that the initial opacity is set to zero so that it is invisible until the cursor is touching it. The properties that appear upon hover are captured by setting &lt;code&gt;hover_alpha&lt;/code&gt; to &lt;code&gt;0.5&lt;/code&gt; along with the &lt;code&gt;hover_fill_color&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;Now you will see a small black circle appear over the original square when hovering over the various markers:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/hover_inspection.6670d76e3ded.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/hover_inspection.6670d76e3ded.gif&quot; width=&quot;628&quot; height=&quot;400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hover_inspection.6670d76e3ded.gif&amp;amp;w=157&amp;amp;sig=c409af5cd8102543a208206f4a55ae0717944ed1 157w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hover_inspection.6670d76e3ded.gif&amp;amp;w=314&amp;amp;sig=d1c5b2b78cf8a32fac1918826f93a6c00324fff0 314w, https://files.realpython.com/media/hover_inspection.6670d76e3ded.gif 628w&quot; sizes=&quot;75vw&quot; alt=&quot;Hover Inspection GIF&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To further explore the capabilities of the &lt;code&gt;HoverTool()&lt;/code&gt;, see the &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/tools.html#hovertool&quot;&gt;HoverTool&lt;/a&gt; and &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#hover-inspections&quot;&gt;Hover Inspections&lt;/a&gt; guides.&lt;/p&gt;
&lt;h3 id=&quot;linking-axes-and-selections&quot;&gt;Linking Axes and Selections&lt;/h3&gt;
&lt;p&gt;Linking is the process of syncing elements of different visualizations within a layout. For instance, maybe you want to link the axes of multiple plots to ensure that if you zoom in on one it is reflected on another. Let&amp;rsquo;s see how it is done.&lt;/p&gt;
&lt;p&gt;For this example, the visualization will be able to pan to different segments of a team&amp;rsquo;s schedule and examine various game stats. Each stat will be represented by its own plot in a two-by-two &lt;code&gt;gridplot()&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;The data can be collected from the &lt;code&gt;team_stats&lt;/code&gt; DataFrame, selecting the Philadelphia 76ers as the team of interest:&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;# Isolate relevant data&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;phi_gm_stats&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;team_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&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;PHI&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; 
                           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;seasTyp&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;Regular&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;loc&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;s1&quot;&gt;&amp;#39;gmDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamPTS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamTRB&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAST&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamTO&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;s1&quot;&gt;&amp;#39;opptPTS&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;sort_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;gmDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add game number&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;phi_gm_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;game_num&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;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;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;phi_gm_stats&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;c1&quot;&gt;# Derive a win_loss column&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;win_loss&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;_&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;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;phi_gm_stats&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterrows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# If the 76ers score more points, it&amp;#39;s a win&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&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;s1&quot;&gt;&amp;#39;teamPTS&amp;#39;&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;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;opptPTS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;win_loss&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;s1&quot;&gt;&amp;#39;W&amp;#39;&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;n&quot;&gt;win_loss&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;s1&quot;&gt;&amp;#39;L&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add the win_loss data to the DataFrame&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;phi_gm_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;winLoss&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;win_loss&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here are the results of the 76ers&amp;rsquo; first 5 games:&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;phi_gm_stats&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;        gmDate  teamPTS  teamTRB  teamAST  teamTO  opptPTS  game_num winLoss&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;10  2017-10-18      115       48       25      17      120         1       L&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;39  2017-10-20       92       47       20      17      102         2       L&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;52  2017-10-21       94       41       18      20      128         3       L&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;80  2017-10-23       97       49       25      21       86         4       W&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;113 2017-10-25      104       43       29      16      105         5       L&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Start by importing the necessary Bokeh libraries, specifying the output parameters, and reading the data into a &lt;code&gt;ColumnDataSource&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;c1&quot;&gt;# Bokeh Libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CategoricalColorMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Div&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.layouts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gridplot&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;c1&quot;&gt;# Output to file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;phi-gm-linked-stats.html&amp;#39;&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;76ers Game Log&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Store the data in a ColumnDataSource&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gm_stats_cds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;phi_gm_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each game is represented by a column, and will be colored green if the result was a win and red for a loss. To accomplish this, Bokeh&amp;rsquo;s &lt;code&gt;CategoricalColorMapper&lt;/code&gt; can be used to map the data values to specified colors:&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;# Create a CategoricalColorMapper that assigns a color to wins and losses&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;win_loss_mapper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CategoricalColorMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factors&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;W&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;L&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
                                         &lt;span class=&quot;n&quot;&gt;palette&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;green&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For this use case, a list specifying the categorical data values to be mapped is passed to &lt;code&gt;factors&lt;/code&gt; and a list with the intended colors to &lt;code&gt;palette&lt;/code&gt;. For more on the &lt;code&gt;CategoricalColorMapper&lt;/code&gt;, see the &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/categorical.html#colors&quot;&gt;Colors&lt;/a&gt; section of &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/categorical.html#handling-categorical-data&quot;&gt;Handling Categorical Data&lt;/a&gt; on Bokeh&amp;rsquo;s User Guide.&lt;/p&gt;
&lt;p&gt;There are four stats to visualize in the two-by-two &lt;code&gt;gridplot&lt;/code&gt;: points, assists, rebounds, and turnovers. In creating the four figures and configuring their respective charts, there is a lot of redundancy in the properties. So to streamline the code a &lt;code&gt;for&lt;/code&gt; loop can be used:&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;# Create a dict with the stat name and its corresponding column in the data&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;stat_names&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;Points&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamPTS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;s1&quot;&gt;&amp;#39;Assists&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAST&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;s1&quot;&gt;&amp;#39;Rebounds&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamTRB&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;s1&quot;&gt;&amp;#39;Turnovers&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamTO&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# The figure for each stat will be held in this dict&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;stat_figs&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;c1&quot;&gt;# For each stat in the dict&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stat_label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stat_col&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stat_names&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Create a figure&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat_label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                 &lt;span class=&quot;n&quot;&gt;plot_height&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;plot_width&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;x_range&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;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tools&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;xpan&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;reset&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&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;c1&quot;&gt;# Configure vbar&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vbar&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;s1&quot;&gt;&amp;#39;game_num&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat_col&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;gm_stats_cds&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;mf&quot;&gt;0.9&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;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;winLoss&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;win_loss_mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Add the figure to stat_figs dict&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stat_figs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat_label&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;fig&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the only parameters that needed to be adjusted were the &lt;code&gt;y-axis-label&lt;/code&gt; of the figure and the data that will dictate &lt;code&gt;top&lt;/code&gt; in the &lt;code&gt;vbar&lt;/code&gt;. These values were easily stored in a &lt;code&gt;dict&lt;/code&gt; that was iterated through to create the figures for each stat.&lt;/p&gt;
&lt;p&gt;You can also see the implementation of the  &lt;code&gt;CategoricalColorMapper&lt;/code&gt;  in the configuration of the &lt;code&gt;vbar&lt;/code&gt; glyph. The &lt;code&gt;color&lt;/code&gt; property is passed a &lt;code&gt;dict&lt;/code&gt; with the field in the &lt;code&gt;ColumnDataSource&lt;/code&gt; to be mapped and the name of the &lt;code&gt;CategoricalColorMapper&lt;/code&gt; created above.&lt;/p&gt;
&lt;p&gt;The initial view will only show the first 10 games of the 76ers&amp;rsquo; season, so there needs to be a way to pan horizontally to navigate through the rest of the games in the season. Thus configuring the toolbar to have an &lt;code&gt;xpan&lt;/code&gt; tool allows panning throughout the plot without having to worry about accidentally skewing the view along the vertical axis.&lt;/p&gt;
&lt;p&gt;Now that the figures are created,  &lt;code&gt;gridplot&lt;/code&gt; can be setup by referencing the figures from the &lt;code&gt;dict&lt;/code&gt; created above:&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;# Create layout&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;grid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gridplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat_figs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Points&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stat_figs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Assists&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;n&quot;&gt;stat_figs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Rebounds&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stat_figs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Turnovers&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Linking the axes of the four plots is as simple as setting the &lt;code&gt;x_range&lt;/code&gt; of each figure equal to one another:&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;# Link together the x-axes&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;stat_figs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Points&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;x_range&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; \
    &lt;span class=&quot;n&quot;&gt;stat_figs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Assists&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;x_range&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; \
    &lt;span class=&quot;n&quot;&gt;stat_figs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Rebounds&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;x_range&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; \
    &lt;span class=&quot;n&quot;&gt;stat_figs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Turnovers&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;x_range&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To add a title bar to the visualization, you could have tried to do this on the points figure, but it would have been limited to the space of that figure. Therefore, a nice trick is to use Bokeh&amp;rsquo;s ability to interpret HTML to insert  a &lt;code&gt;Div&lt;/code&gt; element that contains the title information. Once that is created, simply combine that with the &lt;code&gt;gridplot()&lt;/code&gt; in a &lt;code&gt;column&lt;/code&gt; layout:&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;# Add a title for the entire visualization using Div&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&amp;lt;h3&amp;gt;Philadelphia 76ers Game Log&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&amp;lt;b&amp;gt;&amp;lt;i&amp;gt;2017-18 Regular Season&amp;lt;/i&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&amp;lt;/b&amp;gt;&amp;lt;i&amp;gt;Wins in green, losses in red&amp;lt;/i&amp;gt;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sup_title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Visualize&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;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sup_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Putting all the pieces together results in the following: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/linked_axes.597d060fb6eb.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/linked_axes.597d060fb6eb.gif&quot; width=&quot;884&quot; height=&quot;502&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/linked_axes.597d060fb6eb.gif&amp;amp;w=221&amp;amp;sig=055e6e5afa7b64bf7c2cc0249b981436aa268ac6 221w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/linked_axes.597d060fb6eb.gif&amp;amp;w=442&amp;amp;sig=95039045a70f0680e6675064c3c2daf98f59310c 442w, https://files.realpython.com/media/linked_axes.597d060fb6eb.gif 884w&quot; sizes=&quot;75vw&quot; alt=&quot;Linked Axes GIF&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Similarly you can easily implement linked selections, where a selection on one plot will be reflected on others.&lt;/p&gt;
&lt;p&gt;To see how this works, the next visualization will contain two scatter plots: one that shows the 76ers&amp;rsquo; two-point versus three-point field goal percentage and the other showing the 76ers&amp;rsquo; team points versus opponent points on a game-by-game basis.&lt;/p&gt;
&lt;p&gt;The goal is to be able to select data points on the left-side scatter plot and quickly be able to recognize if the corresponding datapoint on the right scatter plot is a win or loss. &lt;/p&gt;
&lt;p&gt;The DataFrame for this visualization is very similar to that from the first 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;# Isolate relevant data&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;phi_gm_stats_2&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;team_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamAbbr&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;PHI&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; 
                             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;seasTyp&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;Regular&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;loc&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;s1&quot;&gt;&amp;#39;gmDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;s1&quot;&gt;&amp;#39;team2P%&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;s1&quot;&gt;&amp;#39;team3P%&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;s1&quot;&gt;&amp;#39;teamPTS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;s1&quot;&gt;&amp;#39;opptPTS&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;sort_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;gmDate&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add game number&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;phi_gm_stats_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;game_num&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;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;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;phi_gm_stats_2&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;c1&quot;&gt;# Derive a win_loss column&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;win_loss&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;_&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;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;phi_gm_stats_2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterrows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# If the 76ers score more points, it&amp;#39;s a win&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&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;s1&quot;&gt;&amp;#39;teamPTS&amp;#39;&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;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;opptPTS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;win_loss&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;s1&quot;&gt;&amp;#39;W&amp;#39;&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;n&quot;&gt;win_loss&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;s1&quot;&gt;&amp;#39;L&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add the win_loss data to the DataFrame&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;phi_gm_stats_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;winLoss&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;win_loss&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s what the data looks like:&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;phi_gm_stats_2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;        gmDate  team2P%  team3P%  teamPTS  opptPTS  game_num winLoss&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;10  2017-10-18   0.4746   0.4286      115      120         1       L&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;39  2017-10-20   0.4167   0.3125       92      102         2       L&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;52  2017-10-21   0.4138   0.3333       94      128         3       L&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;80  2017-10-23   0.5098   0.3750       97       86         4       W&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;113 2017-10-25   0.5082   0.3333      104      105         5       L&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code to create the visualization is as follows: &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;# Bokeh Libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CategoricalColorMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NumeralTickFormatter&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.layouts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gridplot&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output inline in the notebook&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;phi-gm-linked-selections.html&amp;#39;&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;76ers Percentages vs. Win-Loss&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Store the data in a ColumnDataSource&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gm_stats_cds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;phi_gm_stats_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a CategoricalColorMapper that assigns specific colors to wins and losses&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;win_loss_mapper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CategoricalColorMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factors&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;W&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;L&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;palette&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;Green&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Red&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Specify the tools&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;toolList&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;lasso_select&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;tap&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;reset&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&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;c1&quot;&gt;# Create a figure relating the percentages&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pctFig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&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;2PT FG % vs 3PT FG %, 2017-18 Regular Season&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;plot_width&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toolList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;2PT FG%&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;3PT FG%&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Draw with circle markers&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pctFig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&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;s1&quot;&gt;&amp;#39;team2P%&amp;#39;&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;s1&quot;&gt;&amp;#39;team3P%&amp;#39;&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;gm_stats_cds&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;mi&quot;&gt;12&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;s1&quot;&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Format the y-axis tick labels as percenages&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pctFig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xaxis&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NumeralTickFormatter&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;s1&quot;&gt;&amp;#39;00.0%&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pctFig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaxis&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NumeralTickFormatter&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;s1&quot;&gt;&amp;#39;00.0%&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a figure relating the totals&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;totFig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&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;Team Points vs Opponent Points, 2017-18 Regular Season&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;plot_height&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;plot_width&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toolList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;x_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Team Points&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Opponent Points&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Draw with square markers&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;totFig&lt;/span&gt;&lt;span class=&quot;o&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;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;teamPTS&amp;#39;&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;s1&quot;&gt;&amp;#39;opptPTS&amp;#39;&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;gm_stats_cds&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;mi&quot;&gt;10&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;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;winLoss&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;win_loss_mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create layout&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;grid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gridplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pctFig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;totFig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Visualize&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;grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a great illustration of the power in using a &lt;code&gt;ColumnDataSource&lt;/code&gt;. As long as the glyph renderers (in this case, the &lt;code&gt;circle&lt;/code&gt; glyphs for the percentages, and &lt;code&gt;square&lt;/code&gt; glyphs for the wins and losses) share the same &lt;code&gt;ColumnDataSource&lt;/code&gt;, then the selections will be linked by default. &lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how it looks in action, where you can see selections made on either figure will be reflected on the other:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/linked_selection.70bd761944f3.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/linked_selection.70bd761944f3.gif&quot; width=&quot;844&quot; height=&quot;444&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/linked_selection.70bd761944f3.gif&amp;amp;w=211&amp;amp;sig=e03aa395c2649ce845cab64003640674a1667ef6 211w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/linked_selection.70bd761944f3.gif&amp;amp;w=422&amp;amp;sig=9ca4b33b07636f7074340a026ed29627df40b0e7 422w, https://files.realpython.com/media/linked_selection.70bd761944f3.gif 844w&quot; sizes=&quot;75vw&quot; alt=&quot;Linked Selection GIF&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By selecting a random sample of data points in the upper right quadrant of the left scatter plot, those corresponding to both high two-point and three-point field goal percentage, the data points on the right scatter plot are highlighted. &lt;/p&gt;
&lt;p&gt;Similarly, selecting data points on the right scatter plot that correspond to losses tend to be further to the lower left, lower shooting percentages, on the left scatter plot. &lt;/p&gt;
&lt;p&gt;All the details on linking plots can be found at &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/linking.html&quot;&gt;Linking Plots&lt;/a&gt; in the Bokeh User Guide.&lt;/p&gt;
&lt;h3 id=&quot;highlighting-data-using-the-legend&quot;&gt;Highlighting Data Using the Legend&lt;/h3&gt;
&lt;p&gt;That brings us to the final interactivity example in this tutorial: interactive legends.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;#drawing-data-with-glyphs&quot;&gt;Drawing Data With Glyphs&lt;/a&gt; section, you saw how easy it is to implement a legend when creating your plot. With the legend in place, adding interactivity is merely a matter of assigning a &lt;code&gt;click_policy&lt;/code&gt;. Using a single line of code, you can quickly add the ability to either &lt;code&gt;hide&lt;/code&gt; or &lt;code&gt;mute&lt;/code&gt; data using the legend.&lt;/p&gt;
&lt;p&gt;In this example, you&amp;rsquo;ll see two identical scatter plots comparing the game-by-game points and rebounds of LeBron James and Kevin Durant. The only difference will be that one will use a &lt;code&gt;hide&lt;/code&gt; as its &lt;code&gt;click_policy&lt;/code&gt;, while the other uses &lt;code&gt;mute&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The first step is to configure the output and set up the data, creating a view for each player from the &lt;code&gt;player_stats&lt;/code&gt; DataFrame:&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;# Bokeh Libraries&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.plotting&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GroupFilter&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bokeh.layouts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Output inline in the notebook&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;lebron-vs-durant.html&amp;#39;&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;LeBron James vs. Kevin Durant&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Store the data in a ColumnDataSource&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;player_gm_stats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a view for each player&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;lebron_filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;playFNm&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;LeBron&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;playLNm&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;James&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;lebron_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;player_gm_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lebron_filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;durant_filters&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;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;playFNm&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Kevin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;GroupFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;playLNm&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Durant&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;durant_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CDSView&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;player_gm_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;durant_filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Before creating the figures, the common parameters across the figure, markers, and data can be consolidated into dictionaries and reused. Not only does this save redundancy in the next step, but it provides an easy way to tweak these parameters later if need be:&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;# Consolidate the common keyword arguments in dicts&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;common_figure_kwargs&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;plot_width&amp;#39;&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;s1&quot;&gt;&amp;#39;x_axis_label&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Points&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;toolbar_location&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&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;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;common_circle_kwargs&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;x&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;playPTS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;playTRB&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;source&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_gm_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;size&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;alpha&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.7&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;common_lebron_kwargs&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;view&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lebron_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;#002859&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;legend&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;LeBron James&amp;#39;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;common_durant_kwargs&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;view&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;durant_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;#FFC324&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;legend&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Kevin Durant&amp;#39;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that the various properties are set, the two scatter plots can be built in a much more concise fashion:&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;# Create the two figures and draw the data&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;hide_fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&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;common_figure_kwargs&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;Click Legend to HIDE Data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                  &lt;span class=&quot;n&quot;&gt;y_axis_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Rebounds&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;hide_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&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;common_circle_kwargs&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;common_lebron_kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;hide_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&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;common_circle_kwargs&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;common_durant_kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;mute_fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&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;common_figure_kwargs&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;Click Legend to MUTE Data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mute_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&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;common_circle_kwargs&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;common_lebron_kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;muted_alpha&lt;/span&gt;&lt;span class=&quot;o&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;mute_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&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;common_circle_kwargs&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;common_durant_kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;muted_alpha&lt;/span&gt;&lt;span class=&quot;o&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code&gt;mute_fig&lt;/code&gt; has an extra parameter called &lt;code&gt;muted_alpha&lt;/code&gt;. This parameter controls the opacity of the markers when &lt;code&gt;mute&lt;/code&gt; is used as the &lt;code&gt;click_policy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;click_policy&lt;/code&gt; for each figure is set, and they are shown in a horizontal configuration:&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;# Add interactivity to the legend&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;hide_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;click_policy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;hide&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mute_fig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;click_policy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;mute&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Visualize&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;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hide_fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mute_fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/interactive_legend.8ab895366ef2.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/interactive_legend.8ab895366ef2.gif&quot; width=&quot;878&quot; height=&quot;604&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/interactive_legend.8ab895366ef2.gif&amp;amp;w=219&amp;amp;sig=3f462cc02756cdcb5abd641e4ec56dab9cb9ac03 219w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/interactive_legend.8ab895366ef2.gif&amp;amp;w=439&amp;amp;sig=1401b43e34884a042f3b4911e17aa882a3515e42 439w, https://files.realpython.com/media/interactive_legend.8ab895366ef2.gif 878w&quot; sizes=&quot;75vw&quot; alt=&quot;Interactive Legend GIF&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once the legend is in place, all you have to do is assign either &lt;code&gt;hide&lt;/code&gt; or &lt;code&gt;mute&lt;/code&gt; to the figure&amp;rsquo;s &lt;code&gt;click_policy&lt;/code&gt; property. This will automatically turn your basic legend into an interactive legend.&lt;/p&gt;
&lt;p&gt;Also note that, specifically for &lt;code&gt;mute&lt;/code&gt;, the additional property of &lt;code&gt;muted_alpha&lt;/code&gt; was set in the respective &lt;code&gt;circle&lt;/code&gt; glyphs for LeBron James and Kevin Durant. This dictates the visual effect driven by the legend interaction.&lt;/p&gt;
&lt;p&gt;For more on all things interaction in Bokeh, &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide/interaction.html&quot;&gt;Adding Interactions&lt;/a&gt; in the Bokeh User Guide is a great place to start.&lt;/p&gt;
&lt;h2 id=&quot;summary-and-next-steps&quot;&gt;Summary and Next Steps&lt;/h2&gt;
&lt;p&gt;Congratulations! You&amp;rsquo;ve made it to the end of this tutorial.&lt;/p&gt;
&lt;p&gt;You should now have a great set of tools to start turning your data into beautiful interactive visualizations using Bokeh.&lt;/p&gt;
&lt;p&gt;You learned how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure your script to render to either a static HTML file or Jupyter Notebook&lt;/li&gt;
&lt;li&gt;Instantiate and customize the &lt;code&gt;figure()&lt;/code&gt; object&lt;/li&gt;
&lt;li&gt;Build your visualization using glyphs&lt;/li&gt;
&lt;li&gt;Access and filter your data with the &lt;code&gt;ColumnDataSource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Organize multiple plots in grid and tabbed layouts&lt;/li&gt;
&lt;li&gt;Add different forms of interaction, including selections, hover actions, linking, and interactive legends&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To explore even more of what Bokeh is capable of, the official &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/user_guide.html&quot;&gt;Bokeh User Guide&lt;/a&gt; is an excellent place to dig into some more advanced topics. I&amp;rsquo;d also recommend checking out &lt;a href=&quot;https://bokeh.pydata.org/en/latest/docs/gallery.html&quot;&gt;Bokeh&amp;rsquo;s Gallery&lt;/a&gt; for tons of examples and inspiration.&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 Kenneth Reitz</title>
      <id>https://realpython.com/interview-kenneth-reitz/</id>
      <link href="https://realpython.com/interview-kenneth-reitz/"/>
      <updated>2018-11-14T14:00:00+00:00</updated>
      <summary>Kenneth is the author of the extremely popular requests and pipenv libraries. In this interview, we discuss his latest projects, Responder and PyTheory, and the most challenging code he’s written to date.</summary>
      <content type="html">
        &lt;p&gt;This week, I&amp;rsquo;m excited to be interviewing the prolific &lt;a href=&quot;https://www.kennethreitz.org/&quot;&gt;Kenneth Reitz&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Kenneth is the author of the extremely popular &lt;a href=&quot;http://docs.python-requests.org/en/master/&quot;&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://pipenv.readthedocs.io/en/latest/&quot;&gt;&lt;code&gt;pipenv&lt;/code&gt;&lt;/a&gt; libraries. Join us as we talk about his latest projects and the most challenging code he&amp;rsquo;s written to date. &lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Let’s start at the beginning… How did you get into programming, and when did you start using Python?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3 rounded-circle&quot; src=&quot;https://files.realpython.com/media/kenneth-reitz.578ed9eab0f8.8570d4ac94ef.jpg&quot; width=&quot;667&quot; height=&quot;667&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/kenneth-reitz.578ed9eab0f8.8570d4ac94ef.jpg&amp;amp;w=166&amp;amp;sig=d2adf96bb7d092344fba95a4afd19ffc8ed1d4b7 166w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/kenneth-reitz.578ed9eab0f8.8570d4ac94ef.jpg&amp;amp;w=333&amp;amp;sig=44c2b0c2ca2fe7c76be4c82b7c25ab2c7e14077a 333w, https://files.realpython.com/media/kenneth-reitz.578ed9eab0f8.8570d4ac94ef.jpg 667w&quot; sizes=&quot;75vw&quot; alt=&quot;Kenneth Reitz&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kenneth:&lt;/strong&gt; I started programming at a young age. My dad was a programmer, and I taught myself BASIC and C (with this help) at the age of 9. I started using Python in college, when I took my first CS course. Shortly after, I dropped out and learned many other programming languages, but I always kept coming back to Python.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Congratulations on your new job with &lt;a href=&quot;https://realpython.com/digital-ocean&quot;&gt;Digital Ocean&lt;/a&gt;. You’re the senior member of the Developer Relations team. How are you finding the change in role from your previous job at Heroku, and what can we expect from Digital Ocean moving forward in the Python space?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kenneth:&lt;/strong&gt; Thanks! I’m really enjoying the new role and the opportunity to serve the entire development community, not just the Python community. However, my latest work, &lt;a href=&quot;http://python-responder.org/en/latest/&quot;&gt;Responder&lt;/a&gt;, has been a Digital Ocean project, so there’s room to expect more from us in the Python space 😊&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;You are, of course, most famous for writing the extremely popular &lt;code&gt;requests&lt;/code&gt; library and the new &lt;code&gt;pipenv&lt;/code&gt; library. &lt;a href=&quot;https://www.python.org/&quot;&gt;Python.org&lt;/a&gt; now recommends the use of &lt;code&gt;pipenv&lt;/code&gt; for dependency management. How has the community received &lt;code&gt;pipenv&lt;/code&gt;? Have you seen much resistance from the community with developers preferring to stick to &lt;code&gt;venv&lt;/code&gt; or older methods of dependency management?&lt;/em&gt;&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Kenneth:&lt;/strong&gt; The community has received &lt;code&gt;pipenv&lt;/code&gt; very well, and even companies like GitHub are using its standards for security vulnerability scanning. I haven’t seen much resistance from the community at all, aside from some hatred on reddit. It took me a while to realize that &lt;a href=&quot;https://www.reddit.com/r/python&quot;&gt;/r/python&lt;/a&gt; doesn’t represent the Python community as much as it represents redditors who use Python.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Now hitting 300 million downloads on your requests library is cool and all, but as a guitarist, what I’m more excited about is your latest project &lt;a href=&quot;https://github.com/kennethreitz/pytheory&quot;&gt;PyTheory&lt;/a&gt;. Can you tell us a little about it and your plans for the project going forward?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kenneth:&lt;/strong&gt; PyTheory is a very interesting library that attempts to encapsulate all known musical systems into a library. Currently, there is one system: Western. It can render all the different scales for the Western system, programmatically, and tell you the pitches of the notes (either in decimal or symbolic notation). In addition, there are fretboards and chord charts, so you can specify a custom tuning for your guitar, and generate chord charts with it. It’s very abstract.&lt;/p&gt;
&lt;p&gt;Definitely the most challenging thing I’ve ever written.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;So after recently ditching your Mac for a PC, and turning to Microsoft’s (amazing) &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VS Code&lt;/a&gt; for your Python development, are you happy and proud to be a Windows user? For those who may be reading who haven’t used Windows since Windows ‘95, what are they missing?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kenneth:&lt;/strong&gt; I love the Mac and prefer it to Windows. I’m just currently bored with my setup and decided to challenge myself by running Windows. I’m very happy and productive, though. Feels right at home.&lt;/p&gt;
&lt;p&gt;Windows isn’t what it used to be. It’s a real solid piece of operating system now. I’m running it on my iMac Pro, and it hums like a dream. &lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;I know you’re a keen photographer. How long have you been into it, and what’s your favorite photo you’ve taken? Do you have any other hobbies and interests, aside from Python?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kenneth:&lt;/strong&gt; I’ve been into photography seriously for about 10 years. My favorite photo I’ve ever taken is probably &lt;a href=&quot;https://500px.com/photo/54603002/seasonal-harmonies-by-kenneth-reitz&quot;&gt;this one&lt;/a&gt;. It was taken on a film camera, after I had a migraine for several weeks, and it was my first time being able to walk outside.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Finally, any parting words of wisdom? Anything you’d like to share and/or plug?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kenneth:&lt;/strong&gt; Responder! My new web service framework for Python. It’s ASGI, it’s familiar looking, it’s fast, and it’s easier to use than Flask! Great for building APIs. &lt;a href=&quot;http://python-responder.org/en/latest/&quot;&gt;Check it out!&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Thank you, Kenneth, for joining me this week. It&amp;rsquo;s been great to watch the development of Responder happen in realtime on Twitter. You can follow its development or raise an issue &lt;a href=&quot;https://github.com/kennethreitz/responder&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As always, if there is someone you would like me to interview in the future, reach out to me in the comments below, or &lt;a href=&quot;https://twitter.com/EndlessTrax&quot;&gt;send me a message on Twitter&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>How to Publish an Open-Source Python Package to PyPI</title>
      <id>https://realpython.com/pypi-publish-python-package/</id>
      <link href="https://realpython.com/pypi-publish-python-package/"/>
      <updated>2018-11-12T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you’ll learn how to create a Python package for your project and how to publish it to PyPI, the Python Package Repository.  Quickly get up to speed on everything from naming your package to configuring it using setup.py.</summary>
      <content type="html">
        &lt;p&gt;Python is famous for coming with &lt;a href=&quot;https://docs.python.org/tutorial/stdlib.html#batteries-included&quot;&gt;batteries included&lt;/a&gt;. Sophisticated capabilities are available in the standard library. You can find modules for working with &lt;a href=&quot;https://realpython.com/python-sockets/&quot;&gt;sockets&lt;/a&gt;, parsing &lt;a href=&quot;https://realpython.com/python-csv/&quot;&gt;CSV&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/python-json/&quot;&gt;JSON&lt;/a&gt;, and &lt;a href=&quot;https://docs.python.org/library/xml.html&quot;&gt;XML&lt;/a&gt; files, and working with &lt;a href=&quot;https://docs.python.org/library/shutil.html&quot;&gt;files&lt;/a&gt; and &lt;a href=&quot;https://realpython.com/python-pathlib/&quot;&gt;file paths&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However great the packages included with Python are, there are many fantastic projects available outside the standard library. These are most often hosted at the &lt;a href=&quot;https://pypi.org/&quot;&gt;Python Packaging Index&lt;/a&gt; (PyPI), historically known as the &lt;a href=&quot;https://www.youtube.com/watch?v=B3KBuQHHKx0&quot;&gt;Cheese Shop&lt;/a&gt;. At PyPI, you can find everything from &lt;a href=&quot;https://pypi.org/search/?q=helloworld&quot;&gt;Hello World&lt;/a&gt; to advanced &lt;a href=&quot;https://pypi.org/project/Keras/&quot;&gt;deep learning libraries&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this tutorial, you&amp;rsquo;ll cover how to &lt;strong&gt;upload your own package to PyPI&lt;/strong&gt;. While getting your project published is easier than it used to be, there are still a few steps involved.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You&amp;rsquo;ll learn how to:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prepare your Python package for publication&lt;/li&gt;
&lt;li&gt;Think about versioning&lt;/li&gt;
&lt;li&gt;Upload your package to PyPI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Throughout this tutorial, we&amp;rsquo;ll use a simple example project: a &lt;code&gt;reader&lt;/code&gt; package that can be used to read &lt;em&gt;Real Python&lt;/em&gt; tutorials. The first section introduces this project.&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;a-small-python-package&quot;&gt;A Small Python Package&lt;/h2&gt;
&lt;p&gt;This section will describe a small Python package that we&amp;rsquo;ll use as an example that can be published to PyPI. If you already have a package that you want to publish, feel free to skim this section and join up again at the &lt;a href=&quot;#preparing-your-package-for-publication&quot;&gt;beginning of the next section&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The package that we&amp;rsquo;ll use is called &lt;code&gt;reader&lt;/code&gt; and is an application that can be used to download and read &lt;em&gt;Real Python&lt;/em&gt; articles. If you want to follow along, you can get the full source code from &lt;a href=&quot;https://github.com/realpython/reader&quot;&gt;our GitHub repository&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 source code as shown and explained below is a simplified, but fully functional, version of the &lt;em&gt;Real Python&lt;/em&gt; feed reader. Compared to the package published on &lt;a href=&quot;https://pypi.org/project/realpython-reader/&quot;&gt;PyPI&lt;/a&gt; and &lt;a href=&quot;https://github.com/realpython/reader&quot;&gt;GitHub&lt;/a&gt;, this version lacks some error handling and extra options.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;First, have a look at the directory structure of &lt;code&gt;reader&lt;/code&gt;. The package lives completely inside a directory that is also named &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/
│   ├── config.txt
│   ├── feed.py
│   ├── __init__.py
│   ├── __main__.py
│   └── viewer.py
│
├── tests/
│   ├── test_feed.py
│   └── test_viewer.py
│
├── MANIFEST.in
├── README.md
└── setup.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The source code of the package is in a &lt;code&gt;reader&lt;/code&gt; subdirectory together with a configuration file. There are a few tests in a separate subdirectory. The tests will not be covered here, but you can find them in the &lt;a href=&quot;https://github.com/realpython/reader&quot;&gt;GitHub repository&lt;/a&gt;. To learn more about testing, see Anthony Shaw&amp;rsquo;s great tutorial on &lt;a href=&quot;https://realpython.com/python-testing/&quot;&gt;Getting Started With Testing in Python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re working with your own package, you may use a different structure or have other files in your package directory. Our &lt;a href=&quot;https://realpython.com/python-application-layouts/&quot;&gt;Python Application Layouts&lt;/a&gt; reference discusses several different options. The instructions in this guide will work independently of the layout you use.&lt;/p&gt;
&lt;p&gt;In the rest of this section, you&amp;rsquo;ll see how the &lt;code&gt;reader&lt;/code&gt; package works. In the &lt;a href=&quot;#preparing-your-package-for-publication&quot;&gt;next section&lt;/a&gt;, you&amp;rsquo;ll get a closer look at the special files, including &lt;code&gt;setup.py&lt;/code&gt;, &lt;code&gt;README.md&lt;/code&gt;, and &lt;code&gt;MANIFEST.in&lt;/code&gt;, that are needed to publish your package.&lt;/p&gt;
&lt;h3 id=&quot;using-the-real-python-reader&quot;&gt;Using the Real Python Reader&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;reader&lt;/code&gt; is a very basic &lt;a href=&quot;https://en.wikipedia.org/wiki/Web_feed&quot;&gt;web feed&lt;/a&gt; reader that can download the latest Real Python articles from the &lt;a href=&quot;https://realpython.com/contact/#rss-atom-feed&quot;&gt;Real Python feed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is an example of using the reader to get the list of the latest articles:&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 -m reader
&lt;span class=&quot;go&quot;&gt;The latest tutorials from Real Python (https://realpython.com/)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  0 How to Publish an Open-Source Python Package to PyPI&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  1 Python &amp;quot;while&amp;quot; Loops (Indefinite Iteration)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2 Writing Comments in Python (Guide)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3 Setting Up Python for Machine Learning on Windows&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  4 Python Community Interview With Michael Kennedy&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  5 Practical Text Classification With Python and Keras&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  6 Getting Started With Testing in Python&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  7 Python, Boto3, and AWS S3: Demystified&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  8 Python&amp;#39;s range() Function (Guide)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  9 Python Community Interview With Mike Grouchy&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 10 How to Round Numbers in Python&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 11 Building and Documenting Python REST APIs With Flask and Connexion – Part 2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 12 Splitting, Concatenating, and Joining Strings in Python&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 13 Image Segmentation Using Color Spaces in OpenCV + Python&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 14 Python Community Interview With Mahdi Yusuf&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 15 Absolute vs Relative Imports in Python&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 16 Top 10 Must-Watch PyCon Talks&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 17 Logging in Python&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 18 The Best Python Books&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; 19 Conditional Statements in Python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice that each article is numbered. To read one particular article, you use the same command but include the number of the article as well. For instance, to read &lt;a href=&quot;.&quot;&gt;How to Publish an Open-Source Python Package to PyPI&lt;/a&gt;, you add &lt;code&gt;0&lt;/code&gt; to the 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; python -m reader &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt; How to Publish an Open-Source Python Package to PyPI

&lt;span class=&quot;go&quot;&gt;Python is famous for coming with batteries included. Sophisticated&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;capabilities are available in the standard library. You can find modules&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;for working with sockets, parsing CSV, JSON, and XML files, and&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;working with files and file paths.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;However great the packages included with Python are, there are many&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;fantastic projects available outside the standard library. These are&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;most often hosted at the Python Packaging Index (PyPI), historically&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;known as the Cheese Shop. At PyPI, you can find everything from Hello&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;World to advanced deep learning libraries.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;[... The full text of the article ...]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This prints the full article to the console using the &lt;a href=&quot;https://www.markdownguide.org/basic-syntax&quot;&gt;Markdown&lt;/a&gt; text format.&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;python -m&lt;/code&gt; is used to run a &lt;a href=&quot;https://docs.python.org/library/__main__.html&quot;&gt;library module or package instead of a script&lt;/a&gt;. If you run a package, the contents of the file &lt;code&gt;__main__.py&lt;/code&gt; will be executed. See &lt;a href=&quot;#different-ways-of-calling-a-package&quot;&gt;Different Ways of Calling a Package&lt;/a&gt; for more info.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;By changing the article number, you can read any of the available articles.&lt;/p&gt;
&lt;h3 id=&quot;a-quick-look-at-the-code&quot;&gt;A Quick Look at the Code&lt;/h3&gt;
&lt;p&gt;The details of how &lt;code&gt;reader&lt;/code&gt; works are not important for the purpose of this tutorial. However, if you are interested in seeing the implementation, you can expand the sections below. The package consists of five files:&lt;/p&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_card989b2d&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;#collapse989b2d&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse989b2d&quot;&gt;config.txt&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapse989b2d&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse989b2d&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapse989b2d&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_card989b2d&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;&lt;code&gt;config.txt&lt;/code&gt; is a configuration file used to specify the URL of the &lt;a href=&quot;https://realpython.com/atom.xml&quot;&gt;feed of &lt;em&gt;Real Python&lt;/em&gt; tutorials&lt;/a&gt;. It&amp;rsquo;s a text file that can be read by the &lt;a href=&quot;https://docs.python.org/library/configparser.html&quot;&gt;&lt;code&gt;configparser&lt;/code&gt;&lt;/a&gt; standard library:&lt;/p&gt;
&lt;div class=&quot;highlight ini&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# config.txt&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[feed]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://realpython.com/atom.xml&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In general, such a config file contains key-value pairs separated into sections. This particular file contains only one section (&lt;code&gt;feed&lt;/code&gt;) and one key (&lt;code&gt;url&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; A configuration file is probably overkill for this simple package. We include it here for demonstration purposes.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_card8bc2ed&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;#collapse8bc2ed&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse8bc2ed&quot;&gt;__main__.py&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapse8bc2ed&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse8bc2ed&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapse8bc2ed&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_card8bc2ed&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;The first source code file we&amp;rsquo;ll look at is &lt;code&gt;__main__.py&lt;/code&gt;. The double underscores indicate that this file has a special meaning in Python. Indeed, when running a package as a script with &lt;code&gt;-m&lt;/code&gt; as above, Python executes the contents of the &lt;code&gt;__main__.py&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;In other words, &lt;code&gt;__main__.py&lt;/code&gt; acts as the entry point of our program and takes care of the main flow, calling other parts as needed:&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;# __main__.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;configparser&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConfigParser&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;importlib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Python 3.7+&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;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feed&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewer&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Read the Real Python article feed&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Read URL of the Real Python feed from config file&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cfg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConfigParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_text&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;s2&quot;&gt;&amp;quot;config.txt&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;n&quot;&gt;cfg&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;s2&quot;&gt;&amp;quot;feed&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# If an article ID is given, show the article&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;n&quot;&gt;article&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_article&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;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;n&quot;&gt;viewer&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;article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# If no ID is given, show a list of all articles&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;n&quot;&gt;site&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_site&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;n&quot;&gt;titles&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_titles&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;n&quot;&gt;viewer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;titles&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;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;Notice that &lt;code&gt;main()&lt;/code&gt; is called on the last line. If we do not call &lt;code&gt;main()&lt;/code&gt;, then our program would not do anything. As you saw earlier, the program can either list all articles or print one specific article. This is handled by the &lt;code&gt;if-else&lt;/code&gt; inside &lt;code&gt;main()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To read the URL to the feed from the configuration file, we use &lt;a href=&quot;https://docs.python.org/library/configparser.html&quot;&gt;&lt;code&gt;configparser&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://docs.python.org/library/importlib.html#module-importlib.resources&quot;&gt;&lt;code&gt;importlib.resources&lt;/code&gt;&lt;/a&gt;. The latter is used to import non-code (or resource) files from a package without having to worry about the full file path. It is especially helpful when publishing packages to PyPI where resource files might end up inside binary archives.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;importlib.resources&lt;/code&gt; became a part of the standard library in Python 3.7. If you are using an older version of Python, you can use &lt;a href=&quot;https://importlib-resources.readthedocs.io/&quot;&gt;&lt;code&gt;importlib_resources&lt;/code&gt;&lt;/a&gt; instead. This is a backport compatible with Python 2.7, and 3.4 and above. &lt;code&gt;importlib_resources&lt;/code&gt; can be installed 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 importlib_resources
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;See &lt;a href=&quot;https://www.youtube.com/watch?v=ZsGFU2qh73E&quot;&gt;Barry Warzaw&amp;rsquo;s presentation at PyCon 2018&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_cardcd4fb3&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;#collapsecd4fb3&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapsecd4fb3&quot;&gt;__init__.py&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapsecd4fb3&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapsecd4fb3&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapsecd4fb3&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_cardcd4fb3&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;The next file is &lt;code&gt;__init__.py&lt;/code&gt;. Again, the double underscores in the filename tell us that this is a special file. &lt;code&gt;__init__.py&lt;/code&gt; represents the root of your package. It should usually be kept quite simple, but it&amp;rsquo;s a good place to put package constants, documentation, and so on:&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;# __init__.py&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Version of the realpython-reader package&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;The special variable &lt;code&gt;__version__&lt;/code&gt; is a convention in Python for adding version numbers to your package. It was introduced in &lt;a href=&quot;https://www.python.org/dev/peps/pep-0396/&quot;&gt;PEP 396&lt;/a&gt;. We&amp;rsquo;ll talk &lt;a href=&quot;#versioning-your-package&quot;&gt;more about versioning later&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Variables defined in &lt;code&gt;__init__.py&lt;/code&gt; become available as variables in the package namespace:&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;reader&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;reader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__version__&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;1.0.0&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should define the &lt;code&gt;__version__&lt;/code&gt; variable in your own packages as well.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_card593465&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;#collapse593465&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse593465&quot;&gt;feed.py&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapse593465&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse593465&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapse593465&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_card593465&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;Looking at &lt;code&gt;__main__.py&lt;/code&gt;, you&amp;rsquo;ll see that two modules, &lt;code&gt;feed&lt;/code&gt; and &lt;code&gt;viewer&lt;/code&gt;, are imported and used to read from the feed and show the results. These modules do most of the actual work.&lt;/p&gt;
&lt;p&gt;First consider &lt;code&gt;feed.py&lt;/code&gt;. This file contains functions for reading from a web feed and parsing the result. Luckily there are already great libraries available to do this. &lt;code&gt;feed.py&lt;/code&gt; depends on two modules that are already available on PyPI: &lt;a href=&quot;https://pypi.org/project/feedparser/&quot;&gt;&lt;code&gt;feedparser&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://pypi.org/project/html2text/&quot;&gt;&lt;code&gt;html2text&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;feed.py&lt;/code&gt; contains several functions. We&amp;rsquo;ll discuss them one at a time.&lt;/p&gt;
&lt;p&gt;To avoid reading from the web feed more than necessary, we first create a function that remembers the feed the first time it&amp;rsquo;s read:&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;c1&quot;&gt;# feed.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &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;feedparser&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;html2text&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_CACHED_FEEDS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;dict&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_feed&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;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Only read a feed once, by caching its contents&amp;quot;&amp;quot;&amp;quot;&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;n&quot;&gt;url&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;_CACHED_FEEDS&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;_CACHED_FEEDS&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feedparser&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;url&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_CACHED_FEEDS&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;feedparser.parse()&lt;/code&gt; reads a feed from the web and returns it in a structure that looks like a &lt;a href=&quot;https://realpython.com/python-dicts/&quot;&gt;dictionary&lt;/a&gt;. To avoid downloading the feed more than once, it&amp;rsquo;s stored in &lt;code&gt;_CACHED_FEEDS&lt;/code&gt; and reused for later calls to &lt;code&gt;_feed()&lt;/code&gt;. Both &lt;code&gt;_CACHED_FEEDS&lt;/code&gt; and &lt;code&gt;_feed()&lt;/code&gt; are prefixed by an underscore to indicate that they are support objects not meant to be used directly.&lt;/p&gt;
&lt;p&gt;We can get some basic information about the feed by looking in the &lt;code&gt;.feed&lt;/code&gt; metadata. The following function picks out the title and link to the web site containing the feed:&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;14 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_site&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;lineno&quot;&gt;15 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Get name and link to web site of the feed&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_feed&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;feed&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &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;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{info.title}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{info.link}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In addition to &lt;code&gt;.title&lt;/code&gt; and &lt;code&gt;.link&lt;/code&gt;, attributes like &lt;code&gt;.subtitle&lt;/code&gt;, &lt;code&gt;.updated&lt;/code&gt;, and &lt;code&gt;.id&lt;/code&gt; are &lt;a href=&quot;https://pythonhosted.org/feedparser/common-atom-elements.html&quot;&gt;also available&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The articles available in the feed can be found inside the &lt;code&gt;.entries&lt;/code&gt; list. Article titles can be found with a list comprehension:&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;19 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_titles&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;lineno&quot;&gt;20 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;List titles in feed&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;articles&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_feed&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &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;n&quot;&gt;a&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;articles&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;.entries&lt;/code&gt; lists the articles in the feed sorted chronologically, so that the newest article is &lt;code&gt;.entries[0]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In order to get the contents of one article, we use its index in the &lt;code&gt;.entries&lt;/code&gt; list as an article ID:&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;24 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_article&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;n&quot;&gt;article_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Get article from feed with the given ID&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;articles&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_feed&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;article&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;articles&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;article_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;html2text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;html2text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;30 &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;# &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{article.title}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{text}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After picking the correct article out of the &lt;code&gt;.entries&lt;/code&gt; list, we find the text of the article as HTML on line 28. Next, &lt;code&gt;html2text&lt;/code&gt; does a decent job of translating the HTML into much more readable text. As the HTML doesn&amp;rsquo;t contain the title of the article, the title is added before returning.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_card21cce4&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;#collapse21cce4&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse21cce4&quot;&gt;viewer.py&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapse21cce4&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapse21cce4&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapse21cce4&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_card21cce4&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;The final module is &lt;code&gt;viewer.py&lt;/code&gt;. At the moment, it consists of two very simple functions. In practice, we could have used &lt;code&gt;print()&lt;/code&gt; directly in &lt;code&gt;__main__.py&lt;/code&gt; instead of calling &lt;code&gt;viewer&lt;/code&gt; functions. However, having the functionality split off makes it easier to replace it later with something more advanced. Maybe we could add a GUI interface in a later version?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;viewer.py&lt;/code&gt; contains two functions:&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;# viewer.py&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;article&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;Show one article&amp;quot;&amp;quot;&amp;quot;&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;article&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;show_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;titles&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;Show list of articles&amp;quot;&amp;quot;&amp;quot;&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;The latest tutorials from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{site}&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;article_id&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;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;titles&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;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{article_id:&amp;gt;3}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{title}&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;&lt;code&gt;show()&lt;/code&gt; simply prints one article to the console, while &lt;code&gt;show_list()&lt;/code&gt; prints a list of titles. The latter also creates article IDs that are used when choosing to read one particular article.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;h3 id=&quot;different-ways-of-calling-a-package&quot;&gt;Different Ways of Calling a Package&lt;/h3&gt;
&lt;p&gt;One challenge when your projects grow in complexity is communicating to the user how to use your project. Since the package consists of four different source code files, how does the user know which file to call to run &lt;code&gt;reader&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;python&lt;/code&gt; interpreter program has an &lt;code&gt;-m&lt;/code&gt; option that allows you to specify a module name instead of a file name. For instance, if you have a script called &lt;code&gt;hello.py&lt;/code&gt;, the following two commands are equivalent:&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 hello.py
&lt;span class=&quot;go&quot;&gt;Hi there!&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -m hello
&lt;span class=&quot;go&quot;&gt;Hi there!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One advantage of the latter is that it allows you to call modules that are built into Python as well. One example is calling &lt;a href=&quot;http://python-history.blogspot.com/2010/06/import-antigravity.html&quot;&gt;&lt;code&gt;antigravity&lt;/code&gt;&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; python -m antigravity
&lt;span class=&quot;go&quot;&gt;Created new window in existing browser session.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another advantage of using &lt;code&gt;-m&lt;/code&gt; is that it works for packages as well as modules. As you saw earlier, you can call the &lt;code&gt;reader&lt;/code&gt; package with &lt;code&gt;-m&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 -m reader
&lt;span class=&quot;go&quot;&gt;[...]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;code&gt;reader&lt;/code&gt; is a package, the name only refers to a directory. How does Python decide which code inside that directory to run? It looks for a file named &lt;code&gt;__main__.py&lt;/code&gt;. If such a file exists, it is executed. If &lt;code&gt;__main__.py&lt;/code&gt; does not exist, then an error message is printed:&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 -m math
&lt;span class=&quot;go&quot;&gt;python: No code object available for math&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, you see that the &lt;code&gt;math&lt;/code&gt; standard library has not defined a &lt;code&gt;__main__.py&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;If you are creating a package that is supposed to be executed, you should include a &lt;code&gt;__main__.py&lt;/code&gt; file. &lt;a href=&quot;#configuring-your-package&quot;&gt;Later&lt;/a&gt;, you&amp;rsquo;ll see how you can also create entry points to your package that will behave like regular programs.&lt;/p&gt;
&lt;h2 id=&quot;preparing-your-package-for-publication&quot;&gt;Preparing Your Package for Publication&lt;/h2&gt;
&lt;p&gt;Now you&amp;rsquo;ve got a package you want to publish, or maybe you &lt;a href=&quot;https://github.com/realpython/reader&quot;&gt;copied our package&lt;/a&gt;. Which steps are necessary before you can upload the package to PyPI?&lt;/p&gt;
&lt;h3 id=&quot;naming-your-package&quot;&gt;Naming Your Package&lt;/h3&gt;
&lt;p&gt;The first&amp;mdash;and possibly the hardest&amp;mdash;step is to come up with a good name for your package. All packages on PyPI need to have unique names. With more than 150,000 packages already on PyPI, chances are that your favorite name is already taken.&lt;/p&gt;
&lt;p&gt;You might need to brainstorm and do some research to find the perfect name. Use the &lt;a href=&quot;https://pypi.org/search/&quot;&gt;PyPI search&lt;/a&gt; to check if a name is already taken. The name that you come up with will be visible on PyPI.&lt;/p&gt;
&lt;p&gt;To make the &lt;code&gt;reader&lt;/code&gt; package easier to find on PyPI, we give it a more descriptive name and call it &lt;code&gt;realpython-reader&lt;/code&gt;. The same name will be used to install the package using &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 realpython-reader
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even though we use &lt;code&gt;realpython-reader&lt;/code&gt; as the PyPI name, the package is still called &lt;code&gt;reader&lt;/code&gt; when it&amp;rsquo;s imported:&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;reader&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;help&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&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;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feed&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;feed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_titles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;How to Publish an Open-Source Python Package to PyPI&amp;#39;, ...]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you see, you can use different names for your package on PyPI and when importing. However, if you use the same name or very similar names, then it will be easier for your users.&lt;/p&gt;
&lt;h3 id=&quot;configuring-your-package&quot;&gt;Configuring Your Package&lt;/h3&gt;
&lt;p&gt;In order for your package to be uploaded to PyPI, you need to provide some basic information about it. This information is typically provided in the form of a &lt;code&gt;setup.py&lt;/code&gt; file. There are &lt;a href=&quot;https://www.python.org/dev/peps/pep-0518/&quot;&gt;initiatives&lt;/a&gt; that try to simplify this collection of information. At the moment though, &lt;code&gt;setup.py&lt;/code&gt; is the only fully supported way of providing information about your package.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;setup.py&lt;/code&gt; file should be placed in the top folder of your package. A fairly minimal &lt;code&gt;setup.py&lt;/code&gt; for &lt;code&gt;reader&lt;/code&gt; looks 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;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pathlib&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;setuptools&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# The directory containing this file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;HERE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathlib&lt;/span&gt;&lt;span class=&quot;o&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;vm&quot;&gt;__file__&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;parent&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# The text of the README file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;README&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;HERE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;README.md&amp;quot;&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;read_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# This call to setup() does all the work&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 :: 3&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.7&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;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;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;We will only cover some of the options available in &lt;code&gt;setuptools&lt;/code&gt; here. The &lt;a href=&quot;https://setuptools.readthedocs.io/en/latest/setuptools.html#basic-use&quot;&gt;documentation&lt;/a&gt; does a good job of going into all the detail.&lt;/p&gt;
&lt;p&gt;The parameters that are 100% necessary in the call to &lt;code&gt;setup()&lt;/code&gt; are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;:&lt;/strong&gt; the name of your package as it will appear on PyPI&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;:&lt;/strong&gt; the current version of your package&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;packages&lt;/code&gt;:&lt;/strong&gt; the packages and subpackages containing your source code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will talk &lt;a href=&quot;#versioning-your-package&quot;&gt;more about versions later&lt;/a&gt;. The &lt;code&gt;packages&lt;/code&gt; parameter takes a list of packages. In our example, there is only one package: &lt;code&gt;reader&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You also need to specify any subpackages. In more complicated projects, there might be many packages to list. To simplify this job, &lt;code&gt;setuptools&lt;/code&gt; includes &lt;a href=&quot;https://setuptools.readthedocs.io/en/latest/setuptools.html#using-find-packages&quot;&gt;&lt;code&gt;find_packages()&lt;/code&gt;&lt;/a&gt;, which does a good job of discovering all your subpackages. You could have used &lt;code&gt;find_packages()&lt;/code&gt; in the &lt;code&gt;reader&lt;/code&gt; project as follows:&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;setuptools&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;find_packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setup&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;o&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;n&quot;&gt;find_packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exclude&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;tests&amp;quot;&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;While only &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;, and &lt;code&gt;packages&lt;/code&gt; are required, your package becomes much easier to find on PyPI if you add some more information. Have a look at the &lt;a href=&quot;https://pypi.org/project/realpython-reader/&quot;&gt;&lt;code&gt;realpython-reader&lt;/code&gt; page on PyPI&lt;/a&gt; and compare the information with &lt;code&gt;setup.py&lt;/code&gt; above. All the information comes from &lt;code&gt;setup.py&lt;/code&gt; and &lt;code&gt;README.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pypi_realpython-reader-1.0.0.0dfba15c7278.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/pypi_realpython-reader-1.0.0.0dfba15c7278.png&quot; width=&quot;800&quot; height=&quot;1387&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pypi_realpython-reader-1.0.0.0dfba15c7278.png&amp;amp;w=200&amp;amp;sig=5e3ea991d866da34fe0618f0f28dff5aa07810ed 200w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pypi_realpython-reader-1.0.0.0dfba15c7278.png&amp;amp;w=400&amp;amp;sig=c761a49101526b497da6eec6a6941185ed37b83a 400w, https://files.realpython.com/media/pypi_realpython-reader-1.0.0.0dfba15c7278.png 800w&quot; sizes=&quot;75vw&quot; alt=&quot;Information about the &lt;code&gt;realpython-reader&lt;/code&gt; package at PyPI&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The last two parameters to &lt;code&gt;setup()&lt;/code&gt; deserve special mention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;install_requires&lt;/code&gt;&lt;/strong&gt; is used to list any dependencies your package has to third party libraries. The &lt;code&gt;reader&lt;/code&gt; depends on &lt;code&gt;feedparser&lt;/code&gt; and &lt;code&gt;html2text&lt;/code&gt;, so they should be listed here.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;entry_points&lt;/code&gt;&lt;/strong&gt; is used to create scripts that call a function within your package. In our example, we create a new script &lt;code&gt;realpython&lt;/code&gt; that calls &lt;code&gt;main()&lt;/code&gt; within the &lt;code&gt;reader/__main__.py&lt;/code&gt; file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For another example of a typical setup file, see Kenneth Reitz&amp;rsquo;s &lt;a href=&quot;https://github.com/kennethreitz/setup.py&quot;&gt;&lt;code&gt;setup.py&lt;/code&gt; repository on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;documenting-your-package&quot;&gt;Documenting Your Package&lt;/h3&gt;
&lt;p&gt;Before releasing your package to the world, you should &lt;a href=&quot;https://realpython.com/documenting-python-code/&quot;&gt;add some documentation&lt;/a&gt;. Depending on your package, the documentation can be as small as a simple &lt;code&gt;README&lt;/code&gt; file, or as big as a full web page with tutorials, example galleries, and an API reference.&lt;/p&gt;
&lt;p&gt;At a minimum, you should include a &lt;code&gt;README&lt;/code&gt; file with your project. &lt;a href=&quot;https://dbader.org/blog/write-a-great-readme-for-your-github-project&quot;&gt;A good &lt;code&gt;README&lt;/code&gt;&lt;/a&gt; should quickly describe your project, as well as tell your users how to install and use your package. Typically, you want to include your &lt;code&gt;README&lt;/code&gt; as the &lt;code&gt;long_description&lt;/code&gt; argument to &lt;code&gt;setup()&lt;/code&gt;. This will display your &lt;code&gt;README&lt;/code&gt; on PyPI.&lt;/p&gt;
&lt;p&gt;Traditionally, PyPI has used &lt;a href=&quot;http://docutils.sourceforge.net/rst.html&quot;&gt;reStructuredText&lt;/a&gt; for package documentation. However, since March 2018 &lt;a href=&quot;https://www.markdownguide.org/basic-syntax&quot;&gt;Markdown&lt;/a&gt; has &lt;a href=&quot;https://dustingram.com/articles/2018/03/16/markdown-descriptions-on-pypi&quot;&gt;also been supported&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Outside of PyPI, Markdown is more widely supported than reStructuredText. If you don&amp;rsquo;t need any of the special features of reStructuredText, you&amp;rsquo;ll be better off keeping your &lt;code&gt;README&lt;/code&gt; in Markdown. Note that you should use the &lt;code&gt;setup()&lt;/code&gt; parameter &lt;code&gt;long_description_content_type&lt;/code&gt; to &lt;a href=&quot;https://packaging.python.org/guides/making-a-pypi-friendly-readme/?highlight=long_description_content_type&quot;&gt;tell PyPI which format you are using&lt;/a&gt;. Valid values are &lt;code&gt;text/markdown&lt;/code&gt;, &lt;code&gt;text/x-rst&lt;/code&gt;, and &lt;code&gt;text/plain&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For bigger projects, you might want to offer more documentation than can reasonably fit in a single file. In that case, you can use sites like &lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt; or &lt;a href=&quot;https://readthedocs.org/&quot;&gt;Read the Docs&lt;/a&gt;, and link to the documentation using the &lt;code&gt;url&lt;/code&gt; parameter. In the &lt;code&gt;setup.py&lt;/code&gt; example above, &lt;code&gt;url&lt;/code&gt; is used to link to the &lt;a href=&quot;https://github.com/realpython/reader&quot;&gt;&lt;code&gt;reader&lt;/code&gt; GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;versioning-your-package&quot;&gt;Versioning Your Package&lt;/h3&gt;
&lt;p&gt;Your package needs to have a version, and PyPI will only let you do one upload of a particular version for a package. In other words, if you want to update your package on PyPI, you need to increase the version number first. This is a good thing, as it guarantees reproducibility: two systems with the same version of a package should behave the same.&lt;/p&gt;
&lt;p&gt;There are &lt;a href=&quot;https://en.wikipedia.org/wiki/Software_versioning&quot;&gt;many different schemes&lt;/a&gt; that can be used for your version number. For Python projects, &lt;a href=&quot;https://www.python.org/dev/peps/pep-0440/&quot;&gt;PEP 440&lt;/a&gt; gives some recommendations. However, in order to be flexible, that PEP is complicated. For a simple project, stick with a simple versioning scheme.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://semver.org/&quot;&gt;Semantic versioning&lt;/a&gt; is a good default scheme to use. The version number is given as three numerical components, for instance &lt;code&gt;0.1.2&lt;/code&gt;. The components are called MAJOR, MINOR, and PATCH, and there are simple rules about when to increment each component:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Increment the MAJOR version when you make incompatible API changes.&lt;/li&gt;
&lt;li&gt;Increment the MINOR version when you add functionality in a backwards-compatible manner.&lt;/li&gt;
&lt;li&gt;Increment the PATCH version when you make backwards-compatible bug fixes. (&lt;a href=&quot;https://semver.org/&quot;&gt;Source&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;You may need to specify the version in different files inside your project. In the &lt;code&gt;reader&lt;/code&gt; project, we specified the version both in &lt;code&gt;setup.py&lt;/code&gt; and in &lt;code&gt;reader/__init__.py&lt;/code&gt;. To make sure the version numbers are kept consistent, you can use a tool called &lt;a href=&quot;https://pypi.org/project/bumpversion/&quot;&gt;Bumpversion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can install Bumpversion 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 bumpversion
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To increment the MINOR version of &lt;code&gt;reader&lt;/code&gt;, you would do 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;gp&quot;&gt;$&lt;/span&gt; bumpversion --current-version &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;.0.0 minor setup.py reader/__init__.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This would change the version number from &lt;code&gt;1.0.0&lt;/code&gt; to &lt;code&gt;1.1.0&lt;/code&gt; in both &lt;code&gt;setup.py&lt;/code&gt; and &lt;code&gt;reader/__init__.py&lt;/code&gt;. To simplify the command, you can also give most of the information in a configuration file. See the &lt;a href=&quot;https://pypi.org/project/bumpversion/&quot;&gt;Bumpversion documentation&lt;/a&gt; for details.&lt;/p&gt;
&lt;h3 id=&quot;adding-files-to-your-package&quot;&gt;Adding Files to Your Package&lt;/h3&gt;
&lt;p&gt;Sometimes, you&amp;rsquo;ll have files inside your package that are not source code files. Examples include data files, binaries, documentation, and&amp;mdash;as we have in this project&amp;mdash;configuration files.&lt;/p&gt;
&lt;p&gt;To tell &lt;code&gt;setup()&lt;/code&gt; to include such files, you use a manifest file. For many projects, you don&amp;rsquo;t need to worry about the manifest, as &lt;code&gt;setup()&lt;/code&gt; creates one that includes all code files as well as &lt;code&gt;README&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;If you need to change the manifest, you create a manifest template which must be named &lt;code&gt;MANIFEST.in&lt;/code&gt;. This file specifies rules for what to include and exclude:&lt;/p&gt;
&lt;div class=&quot;highlight text&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;include reader/*.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This example will include all &lt;code&gt;.txt&lt;/code&gt; files in the &lt;code&gt;reader&lt;/code&gt; directory, which in effect is the configuration file. See &lt;a href=&quot;https://docs.python.org/distutils/commandref.html#creating-a-source-distribution-the-sdist-command&quot;&gt;the documentation&lt;/a&gt; for a list of available rules.&lt;/p&gt;
&lt;p&gt;In addition to creating &lt;code&gt;MANIFEST.in&lt;/code&gt;, you also need to tell &lt;code&gt;setup()&lt;/code&gt; to &lt;a href=&quot;https://python-packaging.readthedocs.io/en/latest/non-code-files.html&quot;&gt;copy these non-code files&lt;/a&gt;. This is done by setting the &lt;code&gt;include_package_data&lt;/code&gt; argument to &lt;code&gt;True&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;o&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;o&quot;&gt;...&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;include_package_data&lt;/code&gt; argument controls whether non-code files are copied when your package is installed.&lt;/p&gt;
&lt;h2 id=&quot;publishing-to-pypi&quot;&gt;Publishing to PyPI&lt;/h2&gt;
&lt;p&gt;Your package is finally ready to meet the world outside your computer! In this section, you&amp;rsquo;ll see how to actually upload your package to PyPI.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t already have an account on PyPI, now is the time to create one: &lt;a href=&quot;https://pypi.org/account/register/&quot;&gt;register your account on PyPI&lt;/a&gt;. While you&amp;rsquo;re at it, you should also &lt;a href=&quot;https://test.pypi.org/manage/projects/&quot;&gt;register an account on TestPyPI&lt;/a&gt;. TestPyPI is very useful, as you can try all the steps of publishing a package without any consequences if you mess up.&lt;/p&gt;
&lt;p&gt;To upload your package to PyPI, you&amp;rsquo;ll use a tool called &lt;a href=&quot;https://twine.readthedocs.io&quot;&gt;Twine&lt;/a&gt;. You can install Twine using Pip as usual:&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 twine
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using Twine is quite simple, and you will soon see how to use it to check and publish your package.&lt;/p&gt;
&lt;h3 id=&quot;building-your-package&quot;&gt;Building Your Package&lt;/h3&gt;
&lt;p&gt;Packages on PyPI are not distributed as plain source code. Instead, they are wrapped into distribution packages. The most common formats for distribution packages are source archives and &lt;a href=&quot;https://wheel.readthedocs.io/en/stable/&quot;&gt;Python wheels&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A source archive consists of your source code and any supporting files wrapped into one &lt;a href=&quot;https://en.wikipedia.org/wiki/Tar_%28computing%29&quot;&gt;&lt;code&gt;tar&lt;/code&gt; file&lt;/a&gt;. Similarly, a wheel is essentially a zip archive containing your code. In contrast to the source archive, the wheel includes any extensions ready to use.&lt;/p&gt;
&lt;p&gt;To create a source archive and a wheel for your package, 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; python setup.py sdist bdist_wheel
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create two files in a newly created &lt;code&gt;dist&lt;/code&gt; directory, a source archive and a wheel:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;reader/
│
└── dist/
    ├── realpython_reader-1.0.0-py3-none-any.whl
    └── realpython-reader-1.0.0.tar.gz
&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 Windows, the source archive will be a &lt;code&gt;.zip&lt;/code&gt; file by default. You can choose the format of the source archive &lt;a href=&quot;https://python.readthedocs.io/en/stable/distutils/sourcedist.html&quot;&gt;with the &lt;code&gt;--format&lt;/code&gt; command line option&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You might wonder how &lt;code&gt;setup.py&lt;/code&gt; knows what to do with the &lt;code&gt;sdist&lt;/code&gt; and &lt;code&gt;bdist_wheel&lt;/code&gt; arguments. If you &lt;a href=&quot;#configuring-your-package&quot;&gt;look back&lt;/a&gt; to how &lt;code&gt;setup.py&lt;/code&gt; was implemented, there is no mention of &lt;code&gt;sdist&lt;/code&gt;, &lt;code&gt;bdist_wheel&lt;/code&gt;, or any other command line arguments.&lt;/p&gt;
&lt;p&gt;All the command line arguments are instead implemented in the upstream &lt;a href=&quot;https://github.com/python/cpython/tree/master/Lib/distutils/command&quot;&gt;&lt;code&gt;distutils&lt;/code&gt; standard library&lt;/a&gt;. You can list all available arguments by adding the &lt;code&gt;--help-commands&lt;/code&gt; option:&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 setup.py --help-commands
&lt;span class=&quot;go&quot;&gt;Standard commands:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  build             build everything needed to install&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  build_py          &amp;quot;build&amp;quot; pure Python modules (copy to build directory)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  build_ext         build C/C++ and Cython extensions (compile/link to build directory)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt; ... many more commands ...&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For information about one particular command, you can do something like &lt;code&gt;python setup.py sdist --help&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;testing-your-package&quot;&gt;Testing Your Package&lt;/h3&gt;
&lt;p&gt;First, you should check that the newly built distribution packages contain the files you expect. On Linux and macOS, you should be able to list the contents of the &lt;code&gt;tar&lt;/code&gt; source archive as follows:&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; tar tzf realpython-reader-1.0.0.tar.gz
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/setup.cfg&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/README.md&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/reader/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/reader/feed.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/reader/__init__.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/reader/viewer.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/reader/__main__.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/reader/config.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/PKG-INFO&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/setup.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/MANIFEST.in&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/realpython_reader.egg-info/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/realpython_reader.egg-info/SOURCES.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/realpython_reader.egg-info/requires.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/realpython_reader.egg-info/dependency_links.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/realpython_reader.egg-info/PKG-INFO&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/realpython_reader.egg-info/entry_points.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;realpython-reader-1.0.0/realpython_reader.egg-info/top_level.txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On Windows, you can use a utility like &lt;a href=&quot;https://www.7-zip.org/&quot;&gt;7-zip&lt;/a&gt; to look inside the corresponding &lt;code&gt;zip&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;You should see all your source code listed, as well as a few new files that have been created containing information you provided in &lt;code&gt;setup.py&lt;/code&gt;. In particular, make sure that all subpackages and supporting files are included.&lt;/p&gt;
&lt;p&gt;You can also have a look inside the wheel by unzipping it as if it were a zip file. However, if your source archive contains the files you expect, the wheel should be fine as well.&lt;/p&gt;
&lt;p&gt;Newer versions of Twine (&lt;code&gt;1.12.0&lt;/code&gt; and above) can also check that your package description will render properly on PyPI. You can run &lt;code&gt;twine check&lt;/code&gt; on the files created in &lt;code&gt;dist&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; twine check dist/*
&lt;span class=&quot;go&quot;&gt;Checking distribution dist/realpython_reader-1.0.0-py3-none-any.whl: Passed&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Checking distribution dist/realpython-reader-1.0.0.tar.gz: Passed&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While it won&amp;rsquo;t catch all problems you might run into, it will for instance let you know if you are using the wrong content type.&lt;/p&gt;
&lt;h3 id=&quot;uploading-your-package&quot;&gt;Uploading Your Package&lt;/h3&gt;
&lt;p&gt;Now you&amp;rsquo;re ready to actually upload your package to PyPI. For this, you&amp;rsquo;ll again use the Twine tool, telling it to upload the distribution packages you have built. First, you should upload to &lt;a href=&quot;https://packaging.python.org/guides/using-testpypi/&quot;&gt;TestPyPI&lt;/a&gt; to make sure everything works 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; twine upload --repository-url https://test.pypi.org/legacy/ dist/*
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Twine will ask you for your username and password.&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&amp;rsquo;ve followed the tutorial using the &lt;code&gt;reader&lt;/code&gt; package as an example, the previous command will probably fail with a message saying you are not allowed to upload to the &lt;code&gt;realpython-reader&lt;/code&gt; project.&lt;/p&gt;
&lt;p&gt;You can change the &lt;code&gt;name&lt;/code&gt; in &lt;code&gt;setup.py&lt;/code&gt; to something unique, for example &lt;code&gt;test-your-username&lt;/code&gt;. Then build the project again and upload the newly built files to TestPyPI.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If the upload succeeds, you can quickly head over to &lt;a href=&quot;https://test.pypi.org/&quot;&gt;TestPyPI&lt;/a&gt;, scroll down, and look at your project being proudly displayed among the new releases! Click on your package and make sure everything looks okay.&lt;/p&gt;
&lt;p&gt;If you have been following along using the &lt;code&gt;reader&lt;/code&gt; package, the tutorial ends here! While you can play with TestPyPI as much as you want, you shouldn&amp;rsquo;t upload dummy packages to PyPI just for testing.&lt;/p&gt;
&lt;p&gt;However, if you have your own package to publish, then the moment has finally arrived! With all the preparations taken care of, this final step is short:&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; twine upload dist/*
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Provide your username and password when requested. That&amp;rsquo;s it!&lt;/p&gt;
&lt;p&gt;Head over to &lt;a href=&quot;https://pypi.org/&quot;&gt;PyPI&lt;/a&gt; and look up your package. You can find it either by &lt;a href=&quot;https://pypi.org/search/&quot;&gt;searching&lt;/a&gt;, by looking at the &lt;a href=&quot;https://pypi.org/manage/projects/&quot;&gt;&lt;em&gt;Your projects&lt;/em&gt; page&lt;/a&gt;, or by going directly to the URL of your project: &lt;a href=&quot;https://pypi.org/project/realpython-reader/&quot;&gt;pypi.org/project/your-package-name/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Congratulations! Your package is published on PyPI!&lt;/p&gt;
&lt;h3 id=&quot;pip-install-your-package&quot;&gt;&lt;code&gt;pip install&lt;/code&gt; Your Package&lt;/h3&gt;
&lt;p&gt;Take a moment to bask in the blue glow of the PyPI web page and (of course) brag to your friends.&lt;/p&gt;
&lt;p&gt;Then open up a terminal again. There is one more great pay off!&lt;/p&gt;
&lt;p&gt;With your package uploaded to PyPI, you can install it with &lt;code&gt;pip&lt;/code&gt; 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; pip install your-package-name
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Replace &lt;code&gt;your-package-name&lt;/code&gt; with the name you chose for your package. For instance, to install the &lt;code&gt;reader&lt;/code&gt; package, you would 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 realpython-reader
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Seeing your own code installed by &lt;code&gt;pip&lt;/code&gt; is a wonderful feeling!&lt;/p&gt;
&lt;h2 id=&quot;other-useful-tools&quot;&gt;Other Useful Tools&lt;/h2&gt;
&lt;p&gt;Before wrapping up, there are a few other tools that are useful to know about when creating and publishing Python packages.&lt;/p&gt;
&lt;h3 id=&quot;virtual-environments&quot;&gt;Virtual Environments&lt;/h3&gt;
&lt;p&gt;In this guide, we haven&amp;rsquo;t talked about virtual environments. Virtual environments are very useful when working with different projects, each with their own differing requirements and dependencies.&lt;/p&gt;
&lt;p&gt;See the following guides for more information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer&quot;&gt;Python Virtual Environments: A Primer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/pipenv-guide&quot;&gt;Pipenv: A Guide to the New Python Packaging Tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/products/managing-python-dependencies/&quot;&gt;Managing Python Dependencies With Pip and Virtual Environments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In particular, it&amp;rsquo;s useful to test your package inside a minimal virtual environment to make sure you&amp;rsquo;re including all necessary dependencies in your &lt;code&gt;setup.py&lt;/code&gt; file.&lt;/p&gt;
&lt;h3 id=&quot;cookiecutter&quot;&gt;Cookiecutter&lt;/h3&gt;
&lt;p&gt;One great way to get started with your project is to use &lt;a href=&quot;https://cookiecutter.readthedocs.io/&quot;&gt;Cookiecutter&lt;/a&gt;. It sets up your project by asking you a few questions based on a template. &lt;a href=&quot;https://cookiecutter.readthedocs.io/en/latest/readme.html#a-pantry-full-of-cookiecutters&quot;&gt;Many different templates&lt;/a&gt; are available.&lt;/p&gt;
&lt;p&gt;First, make sure you have Cookiecutter installed on your system. You can install it 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 cookiecutter
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As an example, we&amp;rsquo;ll use the &lt;a href=&quot;https://github.com/kragniz/cookiecutter-pypackage-minimal&quot;&gt;pypackage-minimal&lt;/a&gt; template. To use a template, give Cookiecutter a link to the template:&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; cookiecutter https://github.com/kragniz/cookiecutter-pypackage-minimal
&lt;span class=&quot;go&quot;&gt;author_name [Louis Taylor]: Real Python&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;author_email [louis@kragniz.eu]: office@realpython.com&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;package_name [cookiecutter_pypackage_minimal]: realpython-reader&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;package_version [0.1.0]:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;package_description [...]: Read Real Python tutorials&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;package_url [...]: https://github.com/realpython/reader&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;readme_pypi_badge [True]:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;readme_travis_badge [True]: False&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;readme_travis_url [...]:&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After you have answered a series of questions, Cookiecutter sets up your project. In this example, the template created the following files and directories:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;realpython-reader/
│
├── realpython-reader/
│   └── __init__.py
│
├── tests/
│   ├── __init__.py
│   └── test_sample.py
│
├── README.rst
├── setup.py
└── tox.ini
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://cookiecutter.readthedocs.io/&quot;&gt;Cookiecutter&amp;rsquo;s documentation&lt;/a&gt; is extensive and includes a long list of available cookiecutters, as well as tutorials on how to create your own template.&lt;/p&gt;
&lt;h3 id=&quot;flit&quot;&gt;Flit&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.pypa.io/en/latest/history/&quot;&gt;The history of packaging in Python&lt;/a&gt; is quite messy. One &lt;a href=&quot;https://www.python.org/dev/peps/pep-0518/#rationale&quot;&gt;common criticism&lt;/a&gt; is that using an executable file like &lt;code&gt;setup.py&lt;/code&gt; for configuration information is not ideal.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0518/&quot;&gt;PEP 518&lt;/a&gt; defines an alternative: using a file called &lt;code&gt;pyproject.toml&lt;/code&gt; instead. The &lt;a href=&quot;https://github.com/toml-lang/toml&quot;&gt;TOML format&lt;/a&gt; is a simple configuration file format:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[&amp;hellip;] it is human-usable (unlike &lt;a href=&quot;http://json.org/&quot;&gt;JSON&lt;/a&gt;), it is flexible enough (unlike &lt;a href=&quot;https://docs.python.org/library/configparser.html&quot;&gt;configparser&lt;/a&gt;), stems from a standard (also unlike configparser), and it is not overly complex (unlike &lt;a href=&quot;http://yaml.org/&quot;&gt;YAML&lt;/a&gt;). (&lt;a href=&quot;https://www.python.org/dev/peps/pep-0518/#file-format&quot;&gt;Source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While PEP 518 is already a few years old, the &lt;code&gt;pyproject.toml&lt;/code&gt; configuration file is not yet fully supported in the standard tools.&lt;/p&gt;
&lt;p&gt;However, there are a few new tools that can publish to PyPI based on &lt;code&gt;pyproject.toml&lt;/code&gt;. One such tool is &lt;a href=&quot;https://flit.readthedocs.io/&quot;&gt;Flit&lt;/a&gt;, a great little project that makes it easy to publish simple Python packages. Flit doesn&amp;rsquo;t support advanced packages like those creating C extensions.&lt;/p&gt;
&lt;p&gt;You can &lt;code&gt;pip install flit&lt;/code&gt;, and then start using it as follows:&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; flit init
&lt;span class=&quot;go&quot;&gt;Module name [reader]:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Author []: Real Python&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Author email []: office@realpython.com&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Home page []: https://github.com/realpython/reader&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Choose a license (see http://choosealicense.com/ for more info)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1. MIT - simple and permissive&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2. Apache - explicitly grants patent rights&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3. GPL - ensures that code based on this is shared with the same terms&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4. Skip - choose a license later&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Enter 1-4 [1]:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Written pyproject.toml; edit that file to add optional extra info.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;flit init&lt;/code&gt; command will create a &lt;code&gt;pyproject.toml&lt;/code&gt; file based on the answers you give to a few questions. You might need to edit this file slightly before using it. For the &lt;code&gt;reader&lt;/code&gt; project, the &lt;code&gt;pyproject.toml&lt;/code&gt; file for Flit ends up looking as follows:&lt;/p&gt;
&lt;div class=&quot;highlight ini&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;[build-system]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;requires&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[&amp;quot;flit&amp;quot;]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;build-backend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;flit.buildapi&amp;quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[tool.flit.metadata]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;reader&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;dist-name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;realpython-reader&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;description-file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;README.md&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Real Python&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;author-email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;office@realpython.com&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;home-page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;https://github.com/realpython/reader&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;classifiers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;    &amp;quot;License :: OSI Approved :: MIT License&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;    &amp;quot;Programming Language :: Python :: 3&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;    &amp;quot;Programming Language :: Python :: 3.7&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;requires-python&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;gt;=3.7&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;requires&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[&amp;quot;feedparser&amp;quot;, &amp;quot;html2text&amp;quot;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[tool.flit.scripts]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;realpython&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;reader.__main__:main&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should recognize most of the items from our original &lt;code&gt;setup.py&lt;/code&gt;. One thing to note though is that &lt;code&gt;version&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; are missing. This is not a mistake. Flit actually figures these out itself by using &lt;code&gt;__version__&lt;/code&gt; and the docstring defined in the &lt;code&gt;__init__.py&lt;/code&gt; file. &lt;a href=&quot;https://flit.readthedocs.io/&quot;&gt;Flit&amp;rsquo;s documentation&lt;/a&gt; explains everything about the &lt;code&gt;pyproject.toml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Flit can build your package and even publish it to PyPI. To build your package, simply 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; flit build
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This creates a source archive and a wheel, exactly like &lt;code&gt;python setup.py sdist bdist_wheel&lt;/code&gt; did earlier. To upload your package to PyPI, you can use Twine as earlier. However, you can also use Flit directly:&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; flit publish
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;publish&lt;/code&gt; command will build your package if necessary, and then upload the files to PyPI, prompting you for your username and password if necessary.&lt;/p&gt;
&lt;p&gt;To see Flit in action, have a look at the &lt;a href=&quot;https://www.youtube.com/watch?v=qTgk2DUM6G0&amp;amp;t=11m50s&quot;&gt;2 minute lightning talk&lt;/a&gt; from EuroSciPy 2017. The &lt;a href=&quot;https://flit.readthedocs.io/&quot;&gt;Flit documentation&lt;/a&gt; is a great resource for more information. Brett Cannon&amp;rsquo;s &lt;a href=&quot;https://snarky.ca/a-tutorial-on-python-package-building/&quot;&gt;tutorial on packaging up your Python code for PyPI&lt;/a&gt; includes a section about Flit.&lt;/p&gt;
&lt;h3 id=&quot;poetry&quot;&gt;Poetry&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://poetry.eustace.io/&quot;&gt;Poetry&lt;/a&gt; is another tool that can be used to build and upload your package. It&amp;rsquo;s quite similar to Flit, especially for the things we&amp;rsquo;re looking at here.&lt;/p&gt;
&lt;p&gt;Before you use Poetry, you need to install it. It&amp;rsquo;s possible to &lt;code&gt;pip install poetry&lt;/code&gt; as well. However, the &lt;a href=&quot;https://poetry.eustace.io/docs/#installation&quot;&gt;author recommends&lt;/a&gt; that you use a custom installation script to avoid potential dependency conflicts. See &lt;a href=&quot;https://poetry.eustace.io/docs/#installation&quot;&gt;the documentation&lt;/a&gt; for installation instructions.&lt;/p&gt;
&lt;p&gt;With Poetry installed, you start using it with an &lt;code&gt;init&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; poetry init

&lt;span class=&quot;go&quot;&gt;This command will guide you through creating your pyproject.toml config.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Package name [code]: realpython-reader&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Version [0.1.0]: 1.0.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Description []: Read the latest Real Python tutorials&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create a &lt;code&gt;pyproject.toml&lt;/code&gt; file based on your answers to questions about your package. Unfortunately, the actual specifications inside the &lt;code&gt;pyproject.toml&lt;/code&gt; differ between Flit and Poetry. For Poetry, the &lt;code&gt;pyproject.toml&lt;/code&gt; file ends up looking like the following:&lt;/p&gt;
&lt;div class=&quot;highlight ini&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;[tool.poetry]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;realpython-reader&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;1.0.0&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Read the latest Real Python tutorials&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;readme&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;README.md&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;homepage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;https://github.com/realpython/reader&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;authors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[&amp;quot;Real Python &amp;lt;office@realpython.com&amp;gt;&amp;quot;]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;license&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;MIT&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;packages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[{include = &amp;quot;reader&amp;quot;}]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[&amp;quot;reader/*.txt&amp;quot;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[tool.poetry.dependencies]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;python&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;gt;=3.7&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;feedparser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;gt;=5.2&amp;quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;html2text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;gt;=2018.1&amp;quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[tool.poetry.scripts]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;realpython&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;reader.__main__:main&amp;quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[build-system]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;requires&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[&amp;quot;poetry&amp;gt;=0.12&amp;quot;]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;build-backend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;poetry.masonry.api&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, you should recognize all these items from the earlier discussion of &lt;code&gt;setup.py&lt;/code&gt;. One thing to note is that Poetry will automatically add classifiers based on the license and the version of Python you specify. Poetry also requires you to be explicit about versions of your dependencies. In fact, dependency management is one of the strong points of Poetry.&lt;/p&gt;
&lt;p&gt;Just like Flit, Poetry can build and upload packages to PyPI. The &lt;code&gt;build&lt;/code&gt; command creates a source archive and a wheel:&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; poetry build
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create the two usual files in the &lt;code&gt;dist&lt;/code&gt; subdirectory, which you can upload using Twine as earlier. You can also use Poetry to publish to 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; poetry publish
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will upload your package to PyPI. In addition to building and publishing, Poetry can help you earlier in the process. Similar to Cookiecutter, Poetry can help you start a new project with the &lt;code&gt;new&lt;/code&gt; command. It also supports working with virtual environments. See &lt;a href=&quot;https://poetry.eustace.io/docs/&quot;&gt;Poetry&amp;rsquo;s documentation&lt;/a&gt; for all the details.&lt;/p&gt;
&lt;p&gt;Apart from the slightly different configuration files, Flit and Poetry work very similarly. Poetry is broader in scope as it also aims to help with dependency management, while Flit has been around a little longer. Andrew Pinkham&amp;rsquo;s article &lt;a href=&quot;http://andrewsforge.com/article/python-new-package-landscape/&quot;&gt;Python&amp;rsquo;s New Package Landscape&lt;/a&gt; covers both Flit and Poetry. Poetry was one of the topics at the special &lt;a href=&quot;https://pythonbytes.fm/episodes/show/100/the-big-100-with-special-guests&quot;&gt;100th episode of the Python Bytes podcast&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You now know how to prepare your project and upload it to PyPI, so that it can be installed and used by other people. While there are a few steps you need to go through, seeing your own package on PyPI is a great pay off. Having others find your project useful is even better!&lt;/p&gt;
&lt;p&gt;In this tutorial, you&amp;rsquo;ve seen the steps necessary to publish your own package:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find a good name for your package&lt;/li&gt;
&lt;li&gt;Configure your package using &lt;code&gt;setup.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Build your package&lt;/li&gt;
&lt;li&gt;Upload your package to PyPI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, you&amp;rsquo;ve also seen a few new tools for publishing packages that use the new &lt;code&gt;pyproject.toml&lt;/code&gt; configuration file to simplify the process.&lt;/p&gt;
&lt;p&gt;If you still have questions, feel free to reach out in the comments section below. Also, the &lt;a href=&quot;https://packaging.python.org/&quot;&gt;Python Packaging Authority&lt;/a&gt; has a lot of information with more detail than we covered here.&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 &quot;while&quot; Loops (Indefinite Iteration)</title>
      <id>https://realpython.com/python-while-loop/</id>
      <link href="https://realpython.com/python-while-loop/"/>
      <updated>2018-11-07T14:00:00+00:00</updated>
      <summary>In this tutorial, you&#39;ll learn about indefinite iteration using the Python while loop. You’ll be able to construct basic and complex while loops, interrupt loop execution with break and continue, use the else clause with a while loop, and deal with infinite loops.</summary>
      <content type="html">
        &lt;p&gt;&lt;strong&gt;Iteration&lt;/strong&gt; means executing the same block of code over and over, potentially many times.  A programming structure that implements iteration is called a &lt;strong&gt;loop&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In programming, there are two types of iteration, indefinite and definite:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;With &lt;strong&gt;indefinite iteration&lt;/strong&gt;, the number of times the loop is executed isn&amp;rsquo;t specified explicitly in advance.  Rather, the designated block is executed repeatedly as long as some condition is met.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;With &lt;strong&gt;definite iteration&lt;/strong&gt;, the number of times the designated block will be executed is specified explicitly at the time the loop starts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial, you&amp;rsquo;ll:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learn about the &lt;code&gt;while&lt;/code&gt; loop, the Python control structure used for indefinite iteration&lt;/li&gt;
&lt;li&gt;See how to break out of a loop or loop iteration prematurely&lt;/li&gt;
&lt;li&gt;Explore infinite loops&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you&amp;rsquo;re finished, you should have a good grasp of how to use indefinite iteration in Python.&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-cheat-sheet-experiment&quot; data-focus=&quot;false&quot;&gt;Click here to get our free Python Cheat Sheet&lt;/a&gt; that shows you the basics of Python 3, like working with data types, dictionaries, lists, and Python functions.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-while-loop&quot;&gt;The &lt;code&gt;while&lt;/code&gt; Loop&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s see how Python&amp;rsquo;s &lt;code&gt;while&lt;/code&gt; statement is used to construct loops.  We&amp;rsquo;ll start simple and embellish as we go.&lt;/p&gt;
&lt;p&gt;The format of a rudimentary &lt;code&gt;while&lt;/code&gt; loop is shown 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;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&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;n&quot;&gt;statement&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;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;statement(s)&amp;gt;&lt;/code&gt; represents the block to be repeatedly executed, often referred to as the body of the loop.  This is denoted with indentation, just as in an &lt;code&gt;if&lt;/code&gt; statement.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Remember:&lt;/strong&gt;  All control structures in Python use indentation to define blocks.  See the discussion on &lt;a href=&quot;https://realpython.com/python-conditional-statements/#python-its-all-about-the-indentation&quot;&gt;grouping statements&lt;/a&gt; in the previous tutorial to review.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The controlling expression, &lt;code&gt;&amp;lt;expr&amp;gt;&lt;/code&gt;, typically involves one or more variables that are initialized prior to starting the loop and then modified somewhere in the loop body.  &lt;/p&gt;
&lt;p&gt;When a &lt;code&gt;while&lt;/code&gt; loop is encountered, &lt;code&gt;&amp;lt;expr&amp;gt;&lt;/code&gt; is first evaluated in &lt;a href=&quot;https://realpython.com/python-data-types/#boolean-type-boolean-context-and-truthiness&quot;&gt;Boolean context&lt;/a&gt;.  If it is true, the loop body is executed.  Then &lt;code&gt;&amp;lt;expr&amp;gt;&lt;/code&gt; is checked again, and if still true, the body is executed again.  This continues until &lt;code&gt;&amp;lt;expr&amp;gt;&lt;/code&gt; becomes false, at which point program execution proceeds to the first statement beyond the loop body.&lt;/p&gt;
&lt;p&gt;Consider this loop:&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;lineno&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;n&quot;&gt;n&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&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;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;n&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;lineno&quot;&gt; 4 &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;n&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;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;go&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;go&quot;&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s what&amp;rsquo;s happening in this example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;n&lt;/code&gt; is initially &lt;code&gt;5&lt;/code&gt;.  The expression in the &lt;code&gt;while&lt;/code&gt; statement header on line 2 is &lt;code&gt;n &amp;gt; 0&lt;/code&gt;, which is true, so the loop body executes.  Inside the loop body on line 3, &lt;code&gt;n&lt;/code&gt; is decremented by &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;4&lt;/code&gt;, and then printed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the body of the loop has finished, program execution returns to the top of the loop at line 2, and the expression is evaluated again.  It is still true, so the body executes again, and &lt;code&gt;3&lt;/code&gt; is printed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This continues until &lt;code&gt;n&lt;/code&gt; becomes &lt;code&gt;0&lt;/code&gt;.  At that point, when the expression is tested, it is false, and the loop terminates.  Execution would resume at the first statement following the loop body, but there isn&amp;rsquo;t one in this case.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that the controlling expression of the &lt;code&gt;while&lt;/code&gt; loop is tested first, before anything else happens.  If it&amp;rsquo;s false to start with, the loop body will never be executed at all:&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;n&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;n&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the example above, when the loop is encountered, &lt;code&gt;n&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;.  The controlling expression &lt;code&gt;n &amp;gt; 0&lt;/code&gt; is already false, so the loop body never executes.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s another &lt;code&gt;while&lt;/code&gt; loop involving a list, rather than a numeric comparison:&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;a&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;foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;baz&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;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&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;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&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;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;baz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;bar&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When a &lt;a href=&quot;https://realpython.com/python-operators-expressions/#evaluation-of-non-boolean-values-in-boolean-context&quot;&gt;list is evaluated in Boolean context&lt;/a&gt;, it is truthy if it has elements in it and falsy if it is empty.  In this example, &lt;code&gt;a&lt;/code&gt; is true as long as it has elements in it.  Once all the items have been removed with the &lt;code&gt;.pop()&lt;/code&gt; method and the list is empty, &lt;code&gt;a&lt;/code&gt; is false, and the loop terminates.&lt;/p&gt;
&lt;h2 id=&quot;interruption-of-loop-iteration&quot;&gt;Interruption of Loop Iteration&lt;/h2&gt;
&lt;p&gt;In each example you have seen so far, the entire body of the &lt;code&gt;while&lt;/code&gt; loop is executed on each iteration.  Python provides two keywords that terminate a loop iteration prematurely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;break&lt;/code&gt;&lt;/strong&gt; immediately terminates a loop entirely.  Program execution proceeds to the first statement following the loop body.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;continue&lt;/code&gt;&lt;/strong&gt; immediately terminates the current loop iteration.  Execution jumps to the top of the loop, and the controlling expression is re-evaluated to determine whether the loop will execute again or terminate.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The distinction between &lt;code&gt;break&lt;/code&gt; and &lt;code&gt;continue&lt;/code&gt; is demonstrated in the following diagram:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/t.680f4db5c6a2.png&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/t.680f4db5c6a2.png&quot; width=&quot;708&quot; height=&quot;798&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/t.680f4db5c6a2.png&amp;amp;w=177&amp;amp;sig=2412ff3aaf747a7feb8be5e5d3f53b9f9dbae24e 177w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/t.680f4db5c6a2.png&amp;amp;w=354&amp;amp;sig=7856abd52eeaef2c545cb287e1eb745188954e28 354w, https://files.realpython.com/media/t.680f4db5c6a2.png 708w&quot; sizes=&quot;75vw&quot; alt=&quot;Python while loops: break and continue statements&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;break and continue&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Here&amp;rsquo;s a script file called &lt;code&gt;break.py&lt;/code&gt; that demonstrates the &lt;code&gt;break&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;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&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;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&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;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;n&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;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;n&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;lineno&quot;&gt; 5 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 6 &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;n&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;s1&quot;&gt;&amp;#39;Loop ended.&amp;#39;&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;break.py&lt;/code&gt; from a command-line interpreter produces the following output:&lt;/p&gt;
&lt;div class=&quot;highlight doscon&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;C:\Users\john\Documents&amp;gt;&lt;/span&gt;python break.py
&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Loop ended.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When &lt;code&gt;n&lt;/code&gt; becomes &lt;code&gt;2&lt;/code&gt;, the &lt;code&gt;break&lt;/code&gt; statement is executed.  The loop is terminated completely, and program execution jumps to the &lt;code&gt;print()&lt;/code&gt; statement on line 7.&lt;/p&gt;
&lt;p&gt;The next script, &lt;code&gt;continue.py&lt;/code&gt;, is identical except for a &lt;code&gt;continue&lt;/code&gt; statement in place of the &lt;code&gt;break&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;n&quot;&gt;n&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;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&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;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;n&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;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;n&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;lineno&quot;&gt; 5 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 6 &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;n&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;s1&quot;&gt;&amp;#39;Loop ended.&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 output of &lt;code&gt;continue.py&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight doscon&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;C:\Users\john\Documents&amp;gt;&lt;/span&gt;python continue.py
&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Loop ended.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This time, when &lt;code&gt;n&lt;/code&gt; is &lt;code&gt;2&lt;/code&gt;, the &lt;code&gt;continue&lt;/code&gt; statement causes termination of that iteration.  Thus, &lt;code&gt;2&lt;/code&gt; isn&amp;rsquo;t printed.  Execution returns to the top of the loop, the condition is re-evaluated, and it is still true.  The loop resumes, terminating when &lt;code&gt;n&lt;/code&gt; becomes &lt;code&gt;0&lt;/code&gt;, as previously.&lt;/p&gt;
&lt;h2 id=&quot;the-else-clause&quot;&gt;The &lt;code&gt;else&lt;/code&gt; Clause&lt;/h2&gt;
&lt;p&gt;Python allows an optional &lt;code&gt;else&lt;/code&gt; clause at the end of a &lt;code&gt;while&lt;/code&gt; loop. This is a unique feature of Python, not found in most other programming languages.  The syntax is shown 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;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&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;n&quot;&gt;statement&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;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;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;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;additional_statement&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;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;additional_statement(s)&amp;gt;&lt;/code&gt; specified in the &lt;code&gt;else&lt;/code&gt; clause will be executed when the &lt;code&gt;while&lt;/code&gt; loop terminates.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/t.a370f09e82c4.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-25&quot; src=&quot;https://files.realpython.com/media/t.a370f09e82c4.png&quot; width=&quot;693&quot; height=&quot;576&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/t.a370f09e82c4.png&amp;amp;w=173&amp;amp;sig=602b242071e0c4305243771f5d5ff3068cf6ee3d 173w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/t.a370f09e82c4.png&amp;amp;w=346&amp;amp;sig=a741123a0535ef2ab68295801f23544a27442979 346w, https://files.realpython.com/media/t.a370f09e82c4.png 693w&quot; sizes=&quot;75vw&quot; alt=&quot;thought balloon&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;About now, you may be thinking,  &amp;ldquo;How is that useful?&amp;rdquo; You could accomplish the same thing by putting those statements immediately after the &lt;code&gt;while&lt;/code&gt; loop, without the &lt;code&gt;else&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;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&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;n&quot;&gt;statement&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;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;additional_statement&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;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What&amp;rsquo;s the difference?&lt;/p&gt;
&lt;p&gt;In the latter case, without the &lt;code&gt;else&lt;/code&gt; clause, &lt;code&gt;&amp;lt;additional_statement(s)&amp;gt;&lt;/code&gt; will be executed after the &lt;code&gt;while&lt;/code&gt; loop terminates, no matter what.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;&amp;lt;additional_statement(s)&amp;gt;&lt;/code&gt; are placed in an &lt;code&gt;else&lt;/code&gt; clause, they will be executed only if the loop terminates &amp;ldquo;by exhaustion&amp;rdquo;&amp;mdash;that is, if the loop iterates until the controlling condition becomes false.  If the loop is exited by a &lt;code&gt;break&lt;/code&gt; statement, the &lt;code&gt;else&lt;/code&gt; clause won&amp;rsquo;t be executed.&lt;/p&gt;
&lt;p&gt;Consider the following example:&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;n&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;n&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&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&gt;&lt;span class=&quot;hll&quot;&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;s1&quot;&gt;&amp;#39;Loop done.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Loop done.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, the loop repeated until the condition was exhausted: &lt;code&gt;n&lt;/code&gt; became &lt;code&gt;0&lt;/code&gt;, so &lt;code&gt;n &amp;gt; 0&lt;/code&gt; became false.  Because the loop lived out its natural life, so to speak, the &lt;code&gt;else&lt;/code&gt; clause was executed.  Now observe the difference here:&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;n&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;n&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&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;n&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&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;break&lt;/span&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;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;Loop done.&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;go&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This loop is terminated prematurely with &lt;code&gt;break&lt;/code&gt;, so the &lt;code&gt;else&lt;/code&gt; clause isn&amp;rsquo;t executed.&lt;/p&gt;
&lt;p&gt;It may seem as if the meaning of the word &lt;code&gt;else&lt;/code&gt; doesn&amp;rsquo;t quite fit the &lt;code&gt;while&lt;/code&gt; loop as well as it does the &lt;code&gt;if&lt;/code&gt; statement.  &lt;a href=&quot;https://en.wikipedia.org/wiki/Guido_van_Rossum&quot;&gt;Guido van Rossum&lt;/a&gt;, the creator of Python, has actually said that, if he had it to do over again, he&amp;rsquo;d leave the &lt;code&gt;while&lt;/code&gt; loop&amp;rsquo;s &lt;code&gt;else&lt;/code&gt; clause out of the language.&lt;/p&gt;
&lt;p&gt;One of the following interpretations might help to make it more intuitive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Think of the header of the loop (&lt;code&gt;while n &amp;gt; 0&lt;/code&gt;) as an &lt;code&gt;if&lt;/code&gt; statement (&lt;code&gt;if n &amp;gt; 0&lt;/code&gt;) that gets executed over and over, with the &lt;code&gt;else&lt;/code&gt; clause finally being executed when the condition becomes false.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Think of &lt;code&gt;else&lt;/code&gt; as though it were &lt;code&gt;nobreak&lt;/code&gt;, in that the block that follows gets executed if there wasn&amp;rsquo;t a &lt;code&gt;break&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don&amp;rsquo;t find either of these interpretations helpful, then feel free to ignore them.&lt;/p&gt;
&lt;p&gt;When might an &lt;code&gt;else&lt;/code&gt; clause on a &lt;code&gt;while&lt;/code&gt; loop be useful?  One common situation is if you are searching a list for a specific item.  You can use &lt;code&gt;break&lt;/code&gt; to exit the loop if the item is found, and the &lt;code&gt;else&lt;/code&gt; clause can contain code that is meant to be executed if the item isn&amp;rsquo;t found:&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;a&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;foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;qux&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;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;corge&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;i&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&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;a&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;a&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;n&quot;&gt;s&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;c1&quot;&gt;# Processing for item found&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;span class=&quot;gp&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;mi&quot;&gt;1&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;c1&quot;&gt;# Processing for item not found&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;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;not found in list.&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;go&quot;&gt;corge not found in list.&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; The code shown above is useful to illustrate the concept, but you&amp;rsquo;d actually be very unlikely to search a list that way.&lt;/p&gt;
&lt;p&gt;First of all, lists are usually processed with definite iteration, not a &lt;code&gt;while&lt;/code&gt; loop. Definite iteration is covered in the next tutorial in this series.&lt;/p&gt;
&lt;p&gt;Secondly, Python provides built-in ways to search for an item in a list.  You can use the &lt;code&gt;in&lt;/code&gt; operator:&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;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&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;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;found in list.&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;nb&quot;&gt;print&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;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;not found in list.&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;go&quot;&gt;corge not found in list.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;list.index()&lt;/code&gt; method would also work.  This method raises a &lt;code&gt;ValueError&lt;/code&gt; exception if the item isn&amp;rsquo;t found in the list, so you need to understand exception handling to use it.  In Python, you use a &lt;code&gt;try&lt;/code&gt; statement to handle an exception.  An example is given below:&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;try&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;a&lt;/span&gt;&lt;span class=&quot;o&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;s1&quot;&gt;&amp;#39;corge&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;except&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;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;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;not found in list.&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;go&quot;&gt;corge not found in list.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will learn about exception handling later in this series.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;An &lt;code&gt;else&lt;/code&gt; clause with a &lt;code&gt;while&lt;/code&gt; loop is a bit of an oddity, not often seen.  But don&amp;rsquo;t shy away from it if you find a situation in which you feel it adds clarity to your code!&lt;/p&gt;
&lt;h2 id=&quot;infinite-loops&quot;&gt;Infinite Loops&lt;/h2&gt;
&lt;p&gt;Suppose you write a &lt;code&gt;while&lt;/code&gt; loop that theoretically never ends.  Sounds weird, right?&lt;/p&gt;
&lt;p&gt;Consider this example:&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;while&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;... &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;foo&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;go&quot;&gt;foo&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo&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;  .&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;KeyboardInterrupt&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;pyshell#2&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;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;foo&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 code was terminated by &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-c&quot;&gt;C&lt;/kbd&gt;&lt;/span&gt;, which generates an interrupt from the keyboard.  Otherwise, it would have gone on unendingly.  Many &lt;code&gt;foo&lt;/code&gt; output lines have been removed and replaced by the vertical ellipsis in the output shown.&lt;/p&gt;
&lt;p&gt;Clearly, &lt;code&gt;True&lt;/code&gt; will never be false, or we&amp;rsquo;re all in very big trouble.  Thus, &lt;code&gt;while True:&lt;/code&gt; initiates an infinite loop that will theoretically run forever.&lt;/p&gt;
&lt;p&gt;Maybe that doesn&amp;rsquo;t sound like something you&amp;rsquo;d want to do, but this pattern is actually quite common.  For example, you might write code for a service that starts up and runs forever accepting service requests.  &amp;ldquo;Forever&amp;rdquo; in this context means until you shut it down, or until the heat death of the universe, whichever comes first.&lt;/p&gt;
&lt;p&gt;More prosaically, remember that loops can be broken out of with the &lt;code&gt;break&lt;/code&gt; statement.  It may be more straightforward to terminate a loop based on conditions recognized within the loop body, rather than on a condition evaluated at the top.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s another variant of the loop shown above that successively removes items from a list using &lt;code&gt;.pop()&lt;/code&gt; until it is empty:&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;a&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;foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;baz&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;while&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;... &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;a&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;break&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;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&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;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;baz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;bar&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When &lt;code&gt;a&lt;/code&gt; becomes empty, &lt;code&gt;not a&lt;/code&gt; becomes true, and the &lt;code&gt;break&lt;/code&gt; statement exits the loop.&lt;/p&gt;
&lt;p&gt;You can also specify multiple &lt;code&gt;break&lt;/code&gt; statements in a loop:&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;while&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;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# One condition for loop termination&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Another termination condition&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Yet another&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In cases like this, where there are multiple reasons to end the loop, it is often cleaner to &lt;code&gt;break&lt;/code&gt; out from several different locations, rather than try to specify all the termination conditions in the loop header.&lt;/p&gt;
&lt;p&gt;Infinite loops can be very useful.  Just remember that you must ensure the loop gets broken out of at some point, so it doesn&amp;rsquo;t truly become infinite.&lt;/p&gt;
&lt;h2 id=&quot;nested-while-loops&quot;&gt;Nested &lt;code&gt;while&lt;/code&gt; Loops&lt;/h2&gt;
&lt;p&gt;In general, Python control structures can be nested within one another.  For example, &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;elif&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; conditional statements can be nested:&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;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18&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;gender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;M&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;son&amp;#39;&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;s1&quot;&gt;&amp;#39;daughter&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;M&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;father&amp;#39;&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;s1&quot;&gt;&amp;#39;mother&amp;#39;&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;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;M&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;grandfather&amp;#39;&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;s1&quot;&gt;&amp;#39;grandmother&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Similarly, a &lt;code&gt;while&lt;/code&gt; loop can be contained within another &lt;code&gt;while&lt;/code&gt; loop, as shown here:&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;a&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;foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&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;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&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;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;b&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;baz&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;qux&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;while&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;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&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;s1&quot;&gt;&amp;#39;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&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;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;gt; baz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;gt; qux&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;bar&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;gt; baz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;gt; qux&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A &lt;code&gt;break&lt;/code&gt; or &lt;code&gt;continue&lt;/code&gt; statement found within nested loops applies to the nearest enclosing loop:&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;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Applies to while &amp;lt;expr2&amp;gt;: loop&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Applies to while &amp;lt;expr1&amp;gt;: loop&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Additionally, &lt;code&gt;while&lt;/code&gt; loops can be nested inside &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;elif&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; statements, and vice versa:&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;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;statement&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;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
&lt;/pre&gt;&lt;/div&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;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&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;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;statement&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;n&quot;&gt;statement&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In fact, all the Python control structures can be intermingled with one another to whatever extent you need.  That is as it should be.  Imagine how frustrating it would be if there were unexpected restrictions like &amp;ldquo;A &lt;code&gt;while&lt;/code&gt; loop can&amp;rsquo;t be contained within an &lt;code&gt;if&lt;/code&gt; statement&amp;rdquo; or &amp;ldquo;&lt;code&gt;while&lt;/code&gt; loops can only be nested inside one another at most four deep.&amp;rdquo;  You&amp;rsquo;d have a very difficult time remembering them all.&lt;/p&gt;
&lt;p&gt;Seemingly arbitrary numeric or logical limitations are considered a sign of poor program language design.  Happily, you won&amp;rsquo;t find many in Python.&lt;/p&gt;
&lt;h2 id=&quot;one-line-while-loops&quot;&gt;One-Line &lt;code&gt;while&lt;/code&gt; Loops&lt;/h2&gt;
&lt;p&gt;As with an &lt;code&gt;if&lt;/code&gt; statement, a &lt;code&gt;while&lt;/code&gt; loop can be specified on one line.  If there are multiple statements in the block that makes up the loop body, they can be separated by semicolons (&lt;code&gt;;&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;n&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&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;n&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This only works with simple statements though.  You can&amp;rsquo;t combine two compound statements into one line.  Thus, you can specify a &lt;code&gt;while&lt;/code&gt; loop all on one line as above, and you write an &lt;code&gt;if&lt;/code&gt; statement on one line:&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;kc&quot;&gt;True&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;foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

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

&lt;p&gt;But you can&amp;rsquo;t 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;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&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;n&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;k&quot;&gt;if&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;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;foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;SyntaxError: invalid syntax&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Remember that &lt;a href=&quot;https://www.python.org/dev/peps/pep-0008/#other-recommendations&quot;&gt;PEP 8&lt;/a&gt; discourages multiple statements on one line.  So you probably shouldn&amp;rsquo;t be doing any of this very often anyhow.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this tutorial, you learned about &lt;strong&gt;indefinite iteration&lt;/strong&gt; using the Python &lt;code&gt;while&lt;/code&gt; loop.  You&amp;rsquo;re now able to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Construct basic and complex &lt;code&gt;while&lt;/code&gt; loops&lt;/li&gt;
&lt;li&gt;Interrupt loop execution with &lt;code&gt;break&lt;/code&gt; and &lt;code&gt;continue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;else&lt;/code&gt; clause with a &lt;code&gt;while&lt;/code&gt; loop&lt;/li&gt;
&lt;li&gt;Deal with infinite loops&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should now have a good grasp of how to execute a piece of code repetitively.&lt;/p&gt;
&lt;p&gt;The next tutorial in this series covers &lt;strong&gt;definite iteration&lt;/strong&gt; with &lt;code&gt;for&lt;/code&gt; loops&amp;mdash;recurrent execution where the number of repetitions is specified explicitly.&lt;/p&gt;
&lt;div class=&quot;container py-3 series-nav mb-3&quot;&gt;
  &lt;div class=&quot;row justify-content-between&quot;&gt;
    &lt;div class=&quot;col-12 col-md-3 text-left text-muted ml-1&quot;&gt;&lt;a href=&quot;https://realpython.com/python-conditional-statements/&quot;&gt; «&amp;nbsp;Conditional Statements in Python&lt;/a&gt;&lt;/div&gt;
    &lt;div class=&quot;col-12 col-md-3 text-center text-muted&quot;&gt;&lt;a href=&quot;#&quot;&gt;Python &quot;while&quot; Loops&lt;/a&gt;&lt;/div&gt;
    &lt;div class=&quot;col-12 col-md-3 text-right text-muted mr-1&quot;&gt;&lt;a &gt;Python &quot;for&quot; Loops&amp;nbsp;»&lt;/a&gt;&lt;/div&gt;
  &lt;/div&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>
  

</feed>
