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

  
    <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;Item&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;Item&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;l l-Scalar l-Scalar-Plain&quot;&gt;version&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;2&lt;/span&gt;
&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;jobs&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;build&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;docker&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&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;image&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;circleci/python:3.7&lt;/span&gt;

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

    &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;p p-Indicator&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;l l-Scalar l-Scalar-Plain&quot;&gt;run&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;name&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;install dependencies&lt;/span&gt;
          &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p p-Indicator&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;l l-Scalar l-Scalar-Plain&quot;&gt;run&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;name&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;run tests&lt;/span&gt;
          &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p p-Indicator&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>
  
    <entry>
      <title>Writing Comments in Python (Guide)</title>
      <id>https://realpython.com/python-comments-guide/</id>
      <link href="https://realpython.com/python-comments-guide/"/>
      <updated>2018-11-05T14:00:00+00:00</updated>
      <summary>Learn how to write Python comments that are clean, concise, and useful. Quickly get up to speed on what the best practices are, which types of comments it&#39;s best to avoid, and how you can practice writing cleaner comments.</summary>
      <content type="html">
        &lt;p&gt;When writing code in Python, it&amp;rsquo;s important to make sure that &lt;strong&gt;your code can be easily understood by others&lt;/strong&gt;. Giving variables obvious names, defining explicit functions, and organizing your code are all great ways to do this.&lt;/p&gt;
&lt;p&gt;Another awesome and easy way to increase the readability of your code is by using &lt;strong&gt;comments&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;In this tutorial, you&amp;rsquo;ll cover some of the basics of writing comments in Python. You&amp;rsquo;ll learn how to write comments that are clean and concise, and when you might not need to write any comments at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You&amp;rsquo;ll also learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why it&amp;rsquo;s so important to comment your code&lt;/li&gt;
&lt;li&gt;Best practices for writing comments in Python&lt;/li&gt;
&lt;li&gt;Types of comments you might want to avoid&lt;/li&gt;
&lt;li&gt;How to practice writing cleaner comments&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-commenting-your-code-is-so-important&quot;&gt;Why Commenting Your Code Is So Important&lt;/h2&gt;
&lt;p&gt;Comments are an integral part of any program. They can come in the form of module-level docstrings, or even inline explanations that help shed light on a complex function.&lt;/p&gt;
&lt;p&gt;Before diving into the different types of comments, let&amp;rsquo;s take a closer look at why commenting your code is so important.&lt;/p&gt;
&lt;p&gt;Consider the following two scenarios in which a programmer decided not to comment their code.&lt;/p&gt;
&lt;h3 id=&quot;when-reading-your-own-code&quot;&gt;When Reading Your Own Code&lt;/h3&gt;
&lt;p&gt;Client A wants a last-minute deployment for their web service. You&amp;rsquo;re already on a tight deadline, so you decide to just make it work. All that &amp;ldquo;extra&amp;rdquo; stuff—documentation, proper commenting, and so forth—you&amp;rsquo;ll add that later.&lt;/p&gt;
&lt;p&gt;The deadline comes, and you deploy the service, right on time. &lt;em&gt;Whew!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You make a mental note to go back and update the comments, but before you can put it on your to-do list, your boss comes over with a new project that you need to get started on immediately. Within a few days, you&amp;rsquo;ve completely forgotten that you were supposed to go back and properly comment the code you wrote for Client A.&lt;/p&gt;
&lt;p&gt;Fast forward six months, and Client A needs a patch built for that same service to comply with some new requirements. It&amp;rsquo;s your job to maintain it, since you were the one who built it in the first place. You open up your text editor and&amp;hellip;&lt;/p&gt;
&lt;p&gt;What did you even &lt;em&gt;write?!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You spend hours parsing through your old code, but you&amp;rsquo;re completely lost in the mess. You were in such a rush at the time that you didn&amp;rsquo;t name your variables properly or even set your functions up in the proper control flow. Worst of all, you don&amp;rsquo;t have any comments in the script to tell you what&amp;rsquo;s what!&lt;/p&gt;
&lt;p&gt;Developers forget what their own code does all the time, especially if it was written a long time ago or under a lot of pressure. When a deadline is fast approaching, and hours in front of the computer have led to bloodshot eyes and cramped hands, that pressure can be reflected in the form of code that is messier than usual.&lt;/p&gt;
&lt;p&gt;Once the project is submitted, many developers are simply too tired to go back and comment their code. When it&amp;rsquo;s time to revisit it later down the line, they can spend hours trying to parse through what they wrote.&lt;/p&gt;
&lt;p&gt;Writing comments as you go is a great way to prevent the above scenario from happening. Be nice to Future You!&lt;/p&gt;
&lt;h3 id=&quot;when-others-are-reading-your-code&quot;&gt;When Others Are Reading Your Code&lt;/h3&gt;
&lt;p&gt;Imagine you&amp;rsquo;re the only developer working on &lt;a href=&quot;https://realpython.com/django-setup/&quot;&gt;a small Django project&lt;/a&gt;. You understand your own code pretty well, so you don&amp;rsquo;t tend to use comments or any other sort of documentation, and you like it that way. Comments take time to write and maintain, and you just don&amp;rsquo;t see the point.&lt;/p&gt;
&lt;p&gt;The only problem is, by the end of the year your &amp;ldquo;small Django project&amp;rdquo; has turned into a &amp;ldquo;20,000 lines of code&amp;rdquo; project, and your supervisor is bringing on additional developers to help maintain it.&lt;/p&gt;
&lt;p&gt;The new devs work hard to quickly get up to speed, but within the first few days of working together, you&amp;rsquo;ve realized that they&amp;rsquo;re having some trouble. You used some quirky variable names and wrote with super terse syntax. The new hires spend a lot of time stepping through your code line by line, trying to figure out how it all works. It takes a few days before they can even help you maintain it!&lt;/p&gt;
&lt;p&gt;Using comments throughout your code can help other developers in situations like this one. Comments help other devs skim through your code and gain an understanding of how it all works very quickly. You can help ensure a smooth transition by choosing to comment your code from the outset of a project.&lt;/p&gt;
&lt;h2 id=&quot;how-to-write-comments-in-python&quot;&gt;How to Write Comments in Python&lt;/h2&gt;
&lt;p&gt;Now that you understand why it&amp;rsquo;s so important to comment your code, let&amp;rsquo;s go over some basics so you know how to do it properly.&lt;/p&gt;
&lt;h3 id=&quot;python-commenting-basics&quot;&gt;Python Commenting Basics&lt;/h3&gt;
&lt;p&gt;Comments are for developers. They describe parts of the code where necessary to facilitate the understanding of programmers, including yourself.&lt;/p&gt;
&lt;p&gt;To write a comment in Python, simply put the hash mark &lt;code&gt;#&lt;/code&gt; before your desired 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;c1&quot;&gt;# This is a comment&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Python ignores everything after the hash mark and up to the end of the line. You can insert them anywhere in your code, even inline with other 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;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;This will run.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This won&amp;#39;t run&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you run the above code, you will only see the output &lt;code&gt;This will run.&lt;/code&gt; Everything else is ignored.&lt;/p&gt;
&lt;p&gt;Comments should be short, sweet, and to the point. While &lt;a href=&quot;https://www.python.org/dev/peps/pep-0008/#maximum-line-length&quot;&gt;PEP 8&lt;/a&gt; advises keeping code at 79 characters or fewer per line, it suggests a max of 72 characters for inline comments and docstrings. If your comment is approaching or exceeding that length, then you&amp;rsquo;ll want to spread it out over multiple lines.&lt;/p&gt;
&lt;h3 id=&quot;python-multiline-comments&quot;&gt;Python Multiline Comments&lt;/h3&gt;
&lt;p&gt;Unfortunately, Python doesn&amp;rsquo;t have a way to write multiline comments as you can in languages such as C, Java, and Go:&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;# So you can&amp;#39;t&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;just&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;this&lt;/span&gt;
&lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the above example, the first line will be ignored by the program, but the other lines will raise a Syntax Error.&lt;/p&gt;
&lt;p&gt;In contrast, a language like Java will allow you to spread a comment out over multiple lines quite easily:&lt;/p&gt;
&lt;div class=&quot;highlight java&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* You can easily&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;write multiline&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;comments in Java */&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Everything between &lt;code&gt;/*&lt;/code&gt; and &lt;code&gt;*/&lt;/code&gt; is ignored by the program.&lt;/p&gt;
&lt;p&gt;While Python doesn&amp;rsquo;t have native multiline commenting functionality, you can create multiline comments in Python. There are two simple ways to do so.&lt;/p&gt;
&lt;p&gt;The first way is simply by pressing the &lt;code&gt;return&lt;/code&gt; key after each line, adding a new hash mark and continuing your comment from 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;multiline_example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# This is a pretty good example&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# of how you can spread comments&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# over multiple lines in Python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each line that starts with a hash mark will be ignored by the program.&lt;/p&gt;
&lt;p&gt;Another thing you can do is use multiline strings by wrapping your comment inside a set of triple quotes:&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;If I really hate pressing `enter` and&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;typing all those hash marks, I could&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;just do this instead&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is like multiline comments in Java, where everything enclosed in the triple quotes will function as a comment.&lt;/p&gt;
&lt;p&gt;While this gives you the multiline functionality, this isn&amp;rsquo;t technically a comment. It&amp;rsquo;s a string that&amp;rsquo;s not assigned to any variable, so it&amp;rsquo;s not called or referenced by your program. Still, since it&amp;rsquo;ll be ignored at runtime and won&amp;rsquo;t appear in the bytecode, it can effectively act as a comment. (You can &lt;a href=&quot;https://dbader.org/blog/python-multiline-comment&quot;&gt;take a look at this article&lt;/a&gt; for proof that these strings won&amp;rsquo;t show up in the bytecode.)&lt;/p&gt;
&lt;p&gt;However, be careful where you place these multiline &amp;ldquo;comments.&amp;rdquo; Depending on where they sit in your program, they could turn into &lt;a href=&quot;https://www.geeksforgeeks.org/python-docstrings/&quot;&gt;docstrings&lt;/a&gt;, which are pieces of documentation that are associated with a function or method. If you slip one of these bad boys right after a function definition, then what you intended to be a comment will become associated with that object.&lt;/p&gt;
&lt;p&gt;Be careful where you use these, and when in doubt, just put a hash mark on each subsequent line. If you&amp;rsquo;re interested in learning more about docstrings and how to associate them with modules, classes, and the like, check out our tutorial on &lt;a href=&quot;https://realpython.com/documenting-python-code/&quot;&gt;Documenting Python Code&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;python-commenting-shortcuts&quot;&gt;Python Commenting Shortcuts&lt;/h3&gt;
&lt;p&gt;It can be tedious to type out all those hash marks every time you need to add a comment. So what can you do to speed things up a bit? Here are a few tricks to help you out when commenting.&lt;/p&gt;
&lt;p&gt;One of the first things you can do is use multiple cursors. That&amp;rsquo;s exactly what it sounds like: placing more than one cursor on your screen to accomplish a task. Simply hold down the &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-control&quot;&gt;Ctrl&lt;/kbd&gt;&lt;/span&gt; or &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-command&quot;&gt;Cmd&lt;/kbd&gt;&lt;/span&gt; key while you left-click, and you should see the blinking lines on your screen:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/gif_multi-cursor.6d8f5974124b.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/gif_multi-cursor.6d8f5974124b.gif&quot; width=&quot;496&quot; height=&quot;304&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gif_multi-cursor.6d8f5974124b.gif&amp;amp;w=124&amp;amp;sig=fa97142c906143c1a4e65bb192ff054e51d4576d 124w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gif_multi-cursor.6d8f5974124b.gif&amp;amp;w=248&amp;amp;sig=083e48e89d894737107328b593c78c0cf7d415b5 248w, https://files.realpython.com/media/gif_multi-cursor.6d8f5974124b.gif 496w&quot; sizes=&quot;75vw&quot; alt=&quot;Python Comments Multiple Cursors&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is most effective when you need to comment the same thing in several places.&lt;/p&gt;
&lt;p&gt;What if you&amp;rsquo;ve got a long stretch of text that needs to be commented out? Say you don&amp;rsquo;t want a defined function to run in order to check for a bug. Clicking each and every line to comment it out could take a lot of time! In these cases, you&amp;rsquo;ll want to toggle comments instead. Simply select the desired code and press &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-slash&quot;&gt;/&lt;/kbd&gt;&lt;/span&gt; on PC, or &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-command&quot;&gt;Cmd&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-slash&quot;&gt;/&lt;/kbd&gt;&lt;/span&gt; on Mac:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/gif_toggle.6424d45ed925.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/gif_toggle.6424d45ed925.gif&quot; width=&quot;496&quot; height=&quot;408&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gif_toggle.6424d45ed925.gif&amp;amp;w=124&amp;amp;sig=671dd2df2b937410f49fc70fc54ec846b786dcd8 124w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gif_toggle.6424d45ed925.gif&amp;amp;w=248&amp;amp;sig=518201a0e7ca295a239acb627c2fa6c2e31cff4f 248w, https://files.realpython.com/media/gif_toggle.6424d45ed925.gif 496w&quot; sizes=&quot;75vw&quot; alt=&quot;Python Toggle Comments&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All the highlighted text will be prepended with a hash mark and ignored by the program.&lt;/p&gt;
&lt;p&gt;If your comments are getting too unwieldy, or the comments in a script you&amp;rsquo;re reading are really long, then your text editor may give you the option to collapse them using the small down arrow on the left-hand side:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/gif_hide.6314ae5dad5e.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/gif_hide.6314ae5dad5e.gif&quot; width=&quot;552&quot; height=&quot;280&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gif_hide.6314ae5dad5e.gif&amp;amp;w=138&amp;amp;sig=7e33606cab3d8c59f1f17943f5fe871ad66449b9 138w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gif_hide.6314ae5dad5e.gif&amp;amp;w=276&amp;amp;sig=8a2edbce9e7fc3f0f5b3b9171210993fbbd34dc9 276w, https://files.realpython.com/media/gif_hide.6314ae5dad5e.gif 552w&quot; sizes=&quot;75vw&quot; alt=&quot;Python Hide Comments&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Simply click the arrow to hide the comments. This works best with long comments spread out over multiple lines, or docstrings that take up most of the start of a program.&lt;/p&gt;
&lt;p&gt;Combining these tips will make commenting your code quick, easy, and painless!&lt;/p&gt;
&lt;h2 id=&quot;python-commenting-best-practices&quot;&gt;Python Commenting Best Practices&lt;/h2&gt;
&lt;p&gt;While it&amp;rsquo;s good to know how to write comments in Python, it&amp;rsquo;s just as vital to make sure that your comments are readable and easy to understand.&lt;/p&gt;
&lt;p&gt;Take a look at these tips to help you write comments that really support your code.&lt;/p&gt;
&lt;h3 id=&quot;when-writing-code-for-yourself&quot;&gt;When Writing Code for Yourself&lt;/h3&gt;
&lt;p&gt;You can make life easier for yourself by commenting your own code properly. Even if no one else will ever see it, &lt;em&gt;you&amp;rsquo;ll&lt;/em&gt; see it, and that&amp;rsquo;s enough reason to make it right. You&amp;rsquo;re a developer after all, so your code should be easy for you to understand as well.&lt;/p&gt;
&lt;p&gt;One extremely useful way to use comments for yourself is as an outline for your code. If you&amp;rsquo;re not sure how your program is going to turn out, then you can use comments as a way to keep track of what&amp;rsquo;s left to do, or even as a way of tracking the high-level flow of your program. For instance, use comments to outline a function in pseudo-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;collections&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultdict&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_top_cities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;top_cities&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultdict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# For each price range&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Get city searches in that price&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Count num times city was searched&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Take top 3 cities &amp;amp; add to dict&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&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;top_cities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These comments plan out &lt;code&gt;get_top_cities()&lt;/code&gt;. Once you know exactly what you want your function to do, you can work on translating that to code.&lt;/p&gt;
&lt;p&gt;Using comments like this can help keep everything straight in your head. As you walk through your program, you&amp;rsquo;ll know what&amp;rsquo;s left to do in order to have a fully functional script. After &amp;ldquo;translating&amp;rdquo; the comments to code, remember to remove any comments that have become redundant so that your code stays crisp and clean.&lt;/p&gt;
&lt;p&gt;You can also use comments as part of the debugging process. Comment out the old code and see how that affects your output. If you agree with the change, then don&amp;rsquo;t leave the code commented out in your program, as it decreases readability. Delete it and use version control if you need to bring it back.&lt;/p&gt;
&lt;p&gt;Finally, use comments to define tricky parts of your own code. If you put a project down and come back to it months or years later, you&amp;rsquo;ll spend a lot of time trying to get reacquainted with what you wrote. In case you forget what your own code does, do Future You a favor and mark it down so that it will be easier to get back up to speed later on.&lt;/p&gt;
&lt;h3 id=&quot;when-writing-code-for-others&quot;&gt;When Writing Code for Others&lt;/h3&gt;
&lt;p&gt;People like to skim and jump back and forth through text, and reading code is no different. The only time you&amp;rsquo;ll probably read through code line by line is when it isn&amp;rsquo;t working and you have to figure out what&amp;rsquo;s going on.&lt;/p&gt;
&lt;p&gt;In most other cases, you&amp;rsquo;ll take a quick glance at variables and function definitions in order to get the gist. Having comments to explain what&amp;rsquo;s happening in plain English can really assist a developer in this position.&lt;/p&gt;
&lt;p&gt;Be nice to your fellow devs and use comments to help them skim through your code. Inline comments should be used sparingly to clear up bits of code that aren&amp;rsquo;t obvious on their own. (Of course, your first priority should be to make your code stand on its own, but inline comments can be useful in this regard.)&lt;/p&gt;
&lt;p&gt;If you have a complicated method or function whose name isn&amp;rsquo;t easily understandable, you may want to include a short comment after the &lt;code&gt;def&lt;/code&gt; line to shed some light:&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;complicated_function&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;c1&quot;&gt;# This function does something complicated&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This can help other devs who are skimming your code get a feel for what the function does. &lt;/p&gt;
&lt;p&gt;For any public functions, you&amp;rsquo;ll want to include an associated docstring, whether it&amp;rsquo;s complicated or not:&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;sparsity_ratio&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;array&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;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Return a float&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;    Percentage of values in array that are zero or NaN&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This string will become the &lt;code&gt;.__doc__&lt;/code&gt; attribute of your function and will officially be associated with that specific method. The &lt;a href=&quot;https://www.python.org/dev/peps/pep-0257/#one-line-docstrings&quot;&gt;PEP 257 docstring guidelines&lt;/a&gt; will help you to structure your docstring. These are a set of conventions that developers generally use when structuring docstrings.&lt;/p&gt;
&lt;p&gt;The PEP 257 guidelines have &lt;a href=&quot;https://www.python.org/dev/peps/pep-0257/#multi-line-docstrings&quot;&gt;conventions for multiline docstrings&lt;/a&gt; as well. These docstrings appear right at the top of a file and include a high-level overview of the entire script and what it&amp;rsquo;s supposed to do:&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;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;A module-level docstring&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;Notice the comment above the docstring specifying the encoding.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;Docstrings do appear in the bytecode, so you can access this through&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;the ``__doc__`` attribute. This is also what you&amp;#39;ll see if you call&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;help() on a module or any other Python object.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A module-level docstring like this one will contain any pertinent or need-to-know information for the developer reading it. When writing one, it&amp;rsquo;s recommended to list out all classes, exceptions, and functions as well as a one-line summary for each.&lt;/p&gt;
&lt;h2 id=&quot;python-commenting-worst-practices&quot;&gt;Python Commenting Worst Practices&lt;/h2&gt;
&lt;p&gt;Just as there are standards for writing Python comments, there are a few types of comments that don&amp;rsquo;t lead to Pythonic code. Here are just a few.&lt;/p&gt;
&lt;h3 id=&quot;avoid-wet-comments&quot;&gt;Avoid: W.E.T. Comments&lt;/h3&gt;
&lt;p&gt;Your comments should be D.R.Y. The acronym stands for the programming maxim &amp;ldquo;Don&amp;rsquo;t Repeat Yourself.&amp;rdquo; This means that your code should have little to no redundancy. You don&amp;rsquo;t need to comment a piece of code that sufficiently explains itself, like this one:&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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Returns a&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can clearly see that &lt;code&gt;a&lt;/code&gt; is returned, so there&amp;rsquo;s no need to explicitly state this in a comment. This makes comments W.E.T., meaning you &amp;ldquo;wrote everything twice.&amp;rdquo; (Or, for the more cynical out there, &amp;ldquo;wasted everyone&amp;rsquo;s time.&amp;rdquo;)&lt;/p&gt;
&lt;p&gt;W.E.T. comments can be a simple mistake, especially if you used comments to plan out your code before writing it. But once you&amp;rsquo;ve got the code running well, be sure to go back and remove comments that have become unnecessary.&lt;/p&gt;
&lt;h3 id=&quot;avoid-smelly-comments&quot;&gt;Avoid: Smelly Comments&lt;/h3&gt;
&lt;p&gt;Comments can be a sign of &amp;ldquo;code smell,&amp;rdquo; which is anything that indicates there might be a deeper problem with your code. Code smells try to mask the underlying issues of a program, and comments are one way to try and hide those problems. Comments should support your code, not try to explain it away. If your code is poorly written, no amount of commenting is going to fix it.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take this simple 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;# A dictionary of families who live in each city&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mydict&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;Midtown&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;Powell&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Brantley&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Young&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;Norcross&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;Montgomery&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ackworth&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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;a&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;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# For each city&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;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# If there are no families in the city&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;mydict&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;c1&quot;&gt;# Say that there are no families&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;None.&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 is quite unruly. There&amp;rsquo;s a comment before every line explaining what the code does. This script could have been made simpler by assigning obvious names to variables, functions, and collections, like so:&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;families_by_city&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;Midtown&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;Powell&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Brantley&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Young&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;Norcross&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;Montgomery&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ackworth&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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;no_families&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cities&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;city&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cities&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;families_by_city&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;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;No families in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{city}&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;By using obvious naming conventions, we were able to remove all unnecessary comments and reduce the length of the code as well!&lt;/p&gt;
&lt;p&gt;Your comments should rarely be longer than the code they support. If you&amp;rsquo;re spending too much time explaining what you did, then you need to go back and refactor to make your code more clear and concise.&lt;/p&gt;
&lt;h3 id=&quot;avoid-rude-comments&quot;&gt;Avoid: Rude Comments&lt;/h3&gt;
&lt;p&gt;This is something that&amp;rsquo;s likely to come up when working on a development team. When several people are all working on the same code, others are going to be going in and reviewing what you&amp;rsquo;ve written and making changes. From time to time, you might come across someone who dared to write a comment like this one:&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;# Put this here to fix Ryan&amp;#39;s stupid-a** mistake&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Honestly, it&amp;rsquo;s just a good idea to not do this. It&amp;rsquo;s not okay if it&amp;rsquo;s your friend&amp;rsquo;s code, and you&amp;rsquo;re sure they won&amp;rsquo;t be offended by it. You never know what might get shipped to production, and how is it going to look if you&amp;rsquo;d accidentally left that comment in there, and a client discovered it down the road? You&amp;rsquo;re a professional, and including vulgar words in your comments is not the way to show that.&lt;/p&gt;
&lt;h2 id=&quot;how-to-practice-commenting&quot;&gt;How to Practice Commenting&lt;/h2&gt;
&lt;p&gt;The simplest way to start writing more Pythonic comments is just to do it!&lt;/p&gt;
&lt;p&gt;Start writing comments for yourself in your own code. Make it a point to include simple comments from now on where necessary. Add some clarity to complex functions, and put a docstring at the top of all your scripts.&lt;/p&gt;
&lt;p&gt;Another good way to practice is to go back and review old code that you&amp;rsquo;ve written. See where anything might not make sense, and clean up the code. If it still needs some extra support, add a quick comment to help clarify the code&amp;rsquo;s purpose.&lt;/p&gt;
&lt;p&gt;This is an especially good idea if your code is up on GitHub and people are forking your repo. Help them get started by guiding them through what you&amp;rsquo;ve already done.&lt;/p&gt;
&lt;p&gt;You can also give back to the community by commenting other people&amp;rsquo;s code. If you&amp;rsquo;ve downloaded something from GitHub and had trouble sifting through it, add comments as you come to understand what each piece of code does.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Sign&amp;rdquo; your comment with your initials and the date, and then submit your changes as a pull request. If your changes are merged, you could be helping dozens if not hundreds of developers like yourself get a leg up on their next project.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Learning to comment well is a valuable tool. Not only will you learn how to write more clearly and concisely in general, but you&amp;rsquo;ll no doubt gain a deeper understanding of Python as well.&lt;/p&gt;
&lt;p&gt;Knowing how to write comments in Python can make life easier for all developers, including yourself! They can help other devs get up to speed on what your code does, and help you get re-acquainted with old code of your own.&lt;/p&gt;
&lt;p&gt;By noticing when you&amp;rsquo;re using comments to try and support poorly written code, you&amp;rsquo;ll be able to go back and modify your code to be more robust. Commenting previously written code, whether your own or another developer&amp;rsquo;s, is a great way to practice writing clean comments in Python.&lt;/p&gt;
&lt;p&gt;As you learn more about documenting your code, you can consider moving on to the next level of documentation. Check out our tutorial on &lt;a href=&quot;https://realpython.com/documenting-python-code/&quot;&gt;Documenting Python Code&lt;/a&gt; to take the next step.&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>Setting Up Python for Machine Learning on Windows</title>
      <id>https://realpython.com/python-windows-machine-learning-setup/</id>
      <link href="https://realpython.com/python-windows-machine-learning-setup/"/>
      <updated>2018-10-31T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you’ll cover the basics of setting up a Python numerical computation environment for machine learning on a Windows machine using the Anaconda Python distribution.</summary>
      <content type="html">
        &lt;p&gt;Python has been largely used for numerical and scientific applications in the last years. However, to perform numerical computations in an efficient manner, Python relies on external libraries, sometimes implemented in other languages, such as the &lt;a href=&quot;http://www.numpy.org/&quot;&gt;NumPy&lt;/a&gt; library, which is partly implemented using the Fortran language.&lt;/p&gt;
&lt;p&gt;Due to these dependencies, sometimes it isn&amp;rsquo;t trivial to set up an environment for numerical computations, linking all the necessary libraries. It&amp;rsquo;s common for people to struggle to get things working in workshops involving the use of Python for machine learning, especially when they are using an operating system that lacks a package management system, such as Windows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Walk through the details for setting up a Python environment for numerical computations on a Windows operating system&lt;/li&gt;
&lt;li&gt;Be introduced to Anaconda, a Python distribution proposed to circumvent these setup problems&lt;/li&gt;
&lt;li&gt;See how to install the distribution on a Windows machine and use its tools to manage packages and environments&lt;/li&gt;
&lt;li&gt;Use the installed Python stack to build a neural network and train it to solve a classic classification problem&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;https://realpython.com/optins/view/conda-cheatsheet/&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-conda-cheatsheet&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a Conda cheat sheet&lt;/a&gt; with handy usage examples for managing your Python environment and packages.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;introducing-anaconda-and-conda&quot;&gt;Introducing Anaconda and Conda&lt;/h2&gt;
&lt;p&gt;Since 2011, Python has included &lt;a href=&quot;https://pypi.org/project/pip/&quot;&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt;, a package management system used to install and manage software packages written in Python. However, for numerical computations, there are several dependencies that are not written in Python, so the initial releases of &lt;code&gt;pip&lt;/code&gt; could not solve the problem by themselves.&lt;/p&gt;
&lt;p&gt;To circumvent this problem, Continuum Analytics released &lt;a href=&quot;https://www.anaconda.com/distribution/&quot;&gt;Anaconda&lt;/a&gt;, a Python distribution focused on scientific applications and &lt;a href=&quot;https://conda.io/&quot;&gt;Conda&lt;/a&gt;, a package and environment management system, which is used by the Anaconda distribution. It&amp;rsquo;s worth noticing that the more recent versions of &lt;code&gt;pip&lt;/code&gt; can handle external dependencies using &lt;a href=&quot;https://www.python.org/dev/peps/pep-0427/&quot;&gt;wheels&lt;/a&gt;, but, by using Anaconda, you&amp;rsquo;ll be able to install critical libraries for data science more smoothly. (You can read more on this discussion &lt;a href=&quot;http://jakevdp.github.io/blog/2016/08/25/conda-myths-and-misconceptions/#Myth-#6:-Now-that-pip-uses-wheels,-conda-is-no-longer-necessary&quot;&gt;here&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Although Conda is tightly coupled to the Anaconda Python Distribution, the two are distinct projects with different goals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.anaconda.com/distribution/&quot;&gt;Anaconda&lt;/a&gt;&lt;/strong&gt; is a full distribution of the software in the &lt;a href=&quot;https://pydata.org/&quot;&gt;PyData&lt;/a&gt; ecosystem, including Python itself along with binaries for several third-party open-source projects. Besides Anaconda, there&amp;rsquo;s also &lt;a href=&quot;https://conda.io/miniconda.html&quot;&gt;Miniconda&lt;/a&gt;, which is a minimal Python distribution including basically Conda and its dependencies so that you can install only the packages you need, from scratch&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://conda.io&quot;&gt;Conda&lt;/a&gt;&lt;/strong&gt; is a package, dependency, and environment management system that could be installed without the Anaconda or Miniconda distribution. It runs on Windows, macOS, and Linux and was created for Python programs, but it can package and distribute software for any language. The main purpose is to solve external dependencies issues in an easy way, by downloading pre-compiled versions of software.&lt;/p&gt;
&lt;p&gt;In this sense, it is more like a cross-platform version of a general purpose package manager such as &lt;a href=&quot;https://en.wikipedia.org/wiki/APT_(Debian)&quot;&gt;APT&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Yum_(software)&quot;&gt;YUM&lt;/a&gt;, which helps to find and install packages in a language-agnostic way. Also, Conda is an environment manager, so if you need a package that requires a different version of Python, by using Conda, it is possible to set up a separate environment with a totally different version of Python, maintaining your usual version of Python on your default environment.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&amp;rsquo;s a lot of discussion regarding the creation of another package management system for the Python ecosystem. It&amp;rsquo;s worth mentioning that Conda&amp;rsquo;s creators pushed Python standard packaging to the limit and only created a second tool when it was clear that it was the only reasonable way forward.&lt;/p&gt;
&lt;p&gt;Curiously, even Guido van Rossum, at his speech at the inaugural PyData meetup in 2012, said that, when it comes to packaging, &amp;ldquo;it really sounds like your needs are so unusual compared to the larger Python community that you&amp;rsquo;re just better off building your own.&amp;rdquo; (You can watch a &lt;a href=&quot;https://www.youtube.com/watch?v=QjXJLVINsSA&amp;amp;feature=youtu.be&amp;amp;t=3555&quot;&gt;video of this discussion&lt;/a&gt;.) More information about this discussion can be found &lt;a href=&quot;http://jakevdp.github.io/blog/2016/08/25/conda-myths-and-misconceptions/#Myth-#4:-Creating-conda-in-the-first-place-was-irresponsible-&amp;amp;-divisive&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://technicaldiscovery.blogspot.com/2013/12/why-i-promote-conda.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anaconda and Miniconda have become the most popular Python distributions, widely used for data science and machine learning in various companies and research laboratories. They are free and open source projects and currently include 1400+ packages in the repository. In the following section, we&amp;rsquo;ll go through the installation of the Miniconda Python distribution on a Windows machine.&lt;/p&gt;
&lt;h2 id=&quot;installing-the-miniconda-python-distribution&quot;&gt;Installing the Miniconda Python Distribution&lt;/h2&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll see step-by-step how to set up a data science Python environment on Windows. Instead of the full Anaconda distribution, you&amp;rsquo;ll be using Miniconda to set up a minimal environment containing only Conda and its dependencies, and you&amp;rsquo;ll use that to install the necessary packages.&lt;/p&gt;
&lt;p&gt;The installation processes for Miniconda and Anaconda are very similar. The basic difference is that Anaconda provides an environment with a lot of pre-installed packages, many of which are never used. (You can check the list &lt;a href=&quot;https://docs.anaconda.com/anaconda/packages/py3.6_win-64&quot;&gt;here&lt;/a&gt;.) Miniconda is minimalist and clean, and it allows you to easily install any of Anaconda&amp;rsquo;s packages.&lt;/p&gt;
&lt;p&gt;In this article, the focus will be on using the command line interface (CLI) to set up the packages and environments. However, it&amp;rsquo;s possible to use Conda to install &lt;a href=&quot;https://docs.anaconda.com/anaconda/navigator/&quot;&gt;Anaconda Navigator&lt;/a&gt;, a graphical user interface (GUI), if you wish.&lt;/p&gt;
&lt;p&gt;Miniconda can be installed using an installer available &lt;a href=&quot;https://conda.io/miniconda.html&quot;&gt;here&lt;/a&gt;. You&amp;rsquo;ll notice there are installers for Windows, macOS, and Linux, and for 32-bit or 64-bit operating systems. You should consider the appropriate architecture according to your Windows installation and download the Python 3.x version (at the time of writing this article, 3.7).&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s no reason to use Python 2 on a fresh project anymore, and if you do need Python 2 on some project you&amp;rsquo;re working on, due to some library that has not been updated, it is possible to set up a Python 2 environment using Conda, even if you installed the Miniconda Python 3.x distribution, as you will see in the next section.&lt;/p&gt;
&lt;p&gt;After the download finishes, you just have to run the installer and follow the installation steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click on &lt;em&gt;Next&lt;/em&gt; on the welcome screen:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/miniconda_installer_1.8e05d1208356.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/miniconda_installer_1.8e05d1208356.png&quot; width=&quot;749&quot; height=&quot;582&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_1.8e05d1208356.png&amp;amp;w=187&amp;amp;sig=13661517ceab72d4b453629800ea2bd8d39bafab 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_1.8e05d1208356.png&amp;amp;w=374&amp;amp;sig=3eab1227eef70f6dc9eca6398a6ece7fb3aa8c4a 374w, https://files.realpython.com/media/miniconda_installer_1.8e05d1208356.png 749w&quot; sizes=&quot;75vw&quot; alt=&quot;Miniconda Installer Welcome Screen&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click on &lt;em&gt;I Agree&lt;/em&gt; to agree to the license terms:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/miniconda_installer_2.0541a4c97d48.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/miniconda_installer_2.0541a4c97d48.png&quot; width=&quot;749&quot; height=&quot;582&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_2.0541a4c97d48.png&amp;amp;w=187&amp;amp;sig=d70295f48f51f040f7da00292e087b0689589ead 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_2.0541a4c97d48.png&amp;amp;w=374&amp;amp;sig=f24fcaf491b43907ef25bb2175626259bef38748 374w, https://files.realpython.com/media/miniconda_installer_2.0541a4c97d48.png 749w&quot; sizes=&quot;75vw&quot; alt=&quot;![Miniconda Installer License&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Choose the installation type and click &lt;em&gt;Next&lt;/em&gt;. Another advantage of using Anaconda or Miniconda is that it is possible to install the distribution using a local account. (It isn&amp;rsquo;t necessary to have an administrator account.) If this is the case, choose &lt;em&gt;Just Me&lt;/em&gt;. Otherwise, if you have an administrator account, you may choose &lt;em&gt;All Users&lt;/em&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/miniconda_installer_3.7deacd2d6e61.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/miniconda_installer_3.7deacd2d6e61.png&quot; width=&quot;749&quot; height=&quot;582&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_3.7deacd2d6e61.png&amp;amp;w=187&amp;amp;sig=77c36c7b7e00e4dc3a6ea5539ba4a4157b33467c 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_3.7deacd2d6e61.png&amp;amp;w=374&amp;amp;sig=f9c94649ee1be5e3cca9f4838370b159c7b2ca15 374w, https://files.realpython.com/media/miniconda_installer_3.7deacd2d6e61.png 749w&quot; sizes=&quot;75vw&quot; alt=&quot;iniconda Installer Installation Type&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Choose the install location and click &lt;em&gt;Next&lt;/em&gt;. If you&amp;rsquo;ve chosen to install just for you, the default location will be the folder &lt;em&gt;Miniconda3&lt;/em&gt; under your user&amp;rsquo;s personal folder. It&amp;rsquo;s important not to use spaces in the folder names in the path to Miniconda, since many Python packages have problems when spaces are used in folder names:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/miniconda_installer_4.9eee34622f05.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/miniconda_installer_4.9eee34622f05.png&quot; width=&quot;749&quot; height=&quot;582&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_4.9eee34622f05.png&amp;amp;w=187&amp;amp;sig=6fc8e808499b889f4571b85a5d93a17b214d0472 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_4.9eee34622f05.png&amp;amp;w=374&amp;amp;sig=564f3e0f883a56a53a4f0c8f732f8bf70849df3d 374w, https://files.realpython.com/media/miniconda_installer_4.9eee34622f05.png 749w&quot; sizes=&quot;75vw&quot; alt=&quot;Miniconda Installer Install Location&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;em&gt;Advanced Installation Options&lt;/em&gt;, the suggestion is to use the default choices, which are to not add Anaconda to the PATH environment variable and  to register Anaconda as the default Python. Click &lt;em&gt;Install&lt;/em&gt; to begin installation:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/miniconda_installer_5.8c8633be4ae6.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/miniconda_installer_5.8c8633be4ae6.png&quot; width=&quot;749&quot; height=&quot;582&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_5.8c8633be4ae6.png&amp;amp;w=187&amp;amp;sig=f16af96e843f178016a0b0757deb9051730c8d01 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_5.8c8633be4ae6.png&amp;amp;w=374&amp;amp;sig=47d1c52d08351d2edf87b4b2adc63b23d1b6ea34 374w, https://files.realpython.com/media/miniconda_installer_5.8c8633be4ae6.png 749w&quot; sizes=&quot;75vw&quot; alt=&quot;Miniconda Installer Advanced Installation Options&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Wait while the installer copies the files:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/miniconda_installer_6.769a977abed5.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/miniconda_installer_6.769a977abed5.png&quot; width=&quot;749&quot; height=&quot;582&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_6.769a977abed5.png&amp;amp;w=187&amp;amp;sig=813b48697bb6bd09cdd20a886d72b59ce2a48452 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_6.769a977abed5.png&amp;amp;w=374&amp;amp;sig=9fab72d2909b865430f9c86e2cd49886a2a5fe85 374w, https://files.realpython.com/media/miniconda_installer_6.769a977abed5.png 749w&quot; sizes=&quot;75vw&quot; alt=&quot;Miniconda Installer Installing&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the installation completes, click on &lt;em&gt;Next&lt;/em&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/miniconda_installer_7.5794961b1a9d.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/miniconda_installer_7.5794961b1a9d.png&quot; width=&quot;749&quot; height=&quot;582&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_7.5794961b1a9d.png&amp;amp;w=187&amp;amp;sig=20127e5147395d4a3adc88ba9637004d4729fb4c 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_7.5794961b1a9d.png&amp;amp;w=374&amp;amp;sig=36d499ab461bff7fbb076951bfe400cb7790d068 374w, https://files.realpython.com/media/miniconda_installer_7.5794961b1a9d.png 749w&quot; sizes=&quot;75vw&quot; alt=&quot;Miniconda Installer Installation Complete&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click on &lt;em&gt;Finish&lt;/em&gt; to finish the installation and close the installer:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/miniconda_installer_8.9c04f88bc142.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/miniconda_installer_8.9c04f88bc142.png&quot; width=&quot;749&quot; height=&quot;582&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_8.9c04f88bc142.png&amp;amp;w=187&amp;amp;sig=597b31dd921a82e9333473bdd3bdbc7c6773df93 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/miniconda_installer_8.9c04f88bc142.png&amp;amp;w=374&amp;amp;sig=7db48c94ec31ce53ef73d1680631b0160e5c82de 374w, https://files.realpython.com/media/miniconda_installer_8.9c04f88bc142.png 749w&quot; sizes=&quot;75vw&quot; alt=&quot;Miniconda Installer Finish&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As Anaconda was not included in the PATH environment variable, its commands won&amp;rsquo;t work in the Windows default command prompt. To use the distribution, you should start its own command prompt, which can be done by clicking on the Start button and on &lt;em&gt;Anaconda Prompt&lt;/em&gt; under &lt;em&gt;Anaconda3 (64 bit)&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/anaconda_prompt_start.7e8fc1f5a8c2.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/anaconda_prompt_start.7e8fc1f5a8c2.png&quot; width=&quot;630&quot; height=&quot;588&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/anaconda_prompt_start.7e8fc1f5a8c2.png&amp;amp;w=157&amp;amp;sig=3f1b7f781af48bdedb98ed323cb462ef118970cb 157w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/anaconda_prompt_start.7e8fc1f5a8c2.png&amp;amp;w=315&amp;amp;sig=6d81b2a299cfca638315223a6d84e9e14772991f 315w, https://files.realpython.com/media/anaconda_prompt_start.7e8fc1f5a8c2.png 630w&quot; sizes=&quot;75vw&quot; alt=&quot;Start Anaconda Prompt&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When the prompt opens, you can check if Conda is available by running &lt;code&gt;conda --version&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda --version
&lt;span class=&quot;go&quot;&gt;conda 4.5.11&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To get more information about the installation, you can run &lt;code&gt;conda info&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda info

&lt;span class=&quot;go&quot;&gt;     active environment : base&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    active env location : C:\Users\IEUser\Miniconda3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;            shell level : 1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;       user config file : C:\Users\IEUser\.condarc&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; populated config files : C:\Users\IEUser\.condarc&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;          conda version : 4.5.11&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    conda-build version : not installed&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;         python version : 3.7.0.final.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;       base environment : C:\Users\IEUser\Miniconda3  (writable)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;           channel URLs : https://repo.anaconda.com/pkgs/main/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          https://repo.anaconda.com/pkgs/main/noarch&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          https://repo.anaconda.com/pkgs/free/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          https://repo.anaconda.com/pkgs/free/noarch&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          https://repo.anaconda.com/pkgs/r/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          https://repo.anaconda.com/pkgs/r/noarch&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          https://repo.anaconda.com/pkgs/pro/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          https://repo.anaconda.com/pkgs/pro/noarch&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          https://repo.anaconda.com/pkgs/msys2/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          https://repo.anaconda.com/pkgs/msys2/noarch&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;          package cache : C:\Users\IEUser\Miniconda3\pkgs&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          C:\Users\IEUser\AppData\Local\conda\conda\pkgs&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;       envs directories : C:\Users\IEUser\Miniconda3\envs&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          C:\Users\IEUser\AppData\Local\conda\conda\envs&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          C:\Users\IEUser\.conda\envs&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;               platform : win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;             user-agent : conda/4.5.11 requests/2.19.1 CPython/3.7.0 Windows/10 Windows/10.0.17134&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;          administrator : False&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;             netrc file : None&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;           offline mode : False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that you have Miniconda installed, let&amp;rsquo;s see how Conda environments work.&lt;/p&gt;
&lt;h2 id=&quot;understanding-conda-environments&quot;&gt;Understanding Conda Environments&lt;/h2&gt;
&lt;p&gt;When you start developing a project from scratch, it&amp;rsquo;s recommended that you use the latest versions of the libraries you need. However, when working with someone else&amp;rsquo;s project, such as when running an example from &lt;a href=&quot;https://www.kaggle.com/&quot;&gt;Kaggle&lt;/a&gt; or &lt;a href=&quot;https://github.com/&quot;&gt;Github&lt;/a&gt;, you may need to install specific versions of packages or even another version of Python due to compatibility issues.&lt;/p&gt;
&lt;p&gt;This problem may also occur when you try to run an application you&amp;rsquo;ve developed long ago, which uses a particular library version that does not work with your application anymore due to updates.&lt;/p&gt;
&lt;p&gt;Virtual environments are a solution to this kind of problem. By using them, it is possible to create multiple environments, each one with different versions of packages. A typical Python set up includes &lt;a href=&quot;https://virtualenv.pypa.io/en/stable/#&quot;&gt;Virtualenv&lt;/a&gt;, a tool to create isolated Python virtual environments, widely used in the Python community.&lt;/p&gt;
&lt;p&gt;Conda includes its own environment manager and presents some advantages over Virtualenv, especially concerning numerical applications, such as the ability to manage non-Python dependencies and the ability to manage different versions of Python, which is not possible with Virtualenv. Besides that, Conda environments are entirely compatible with default Python packages that may be installed using &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Miniconda installation provides Conda and a root environment with a version of Python and some basic packages installed. Besides this root environment, it is possible to set up additional environments including different versions of Python and packages.&lt;/p&gt;
&lt;p&gt;Using the Anaconda prompt, it is possible to check the available Conda environments by running &lt;code&gt;conda env list&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda env list
&lt;span class=&quot;go&quot;&gt;# conda environments:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;base                  *  C:\Users\IEUser\Miniconda3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This base environment is the root environment, created by the Miniconda installer. It is possible to create another environment, named &lt;code&gt;otherenv&lt;/code&gt;, by running &lt;code&gt;conda create --name otherenv&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda create --name otherenv
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3\envs\otherenv&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)? y&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Preparing transaction: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Verifying transaction: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Executing transaction: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# To activate this environment, use&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#     $ conda activate otherenv&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# To deactivate an active environment, use&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#     $ conda deactivate&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As notified after the environment creation process is finished, it is possible to activate the &lt;code&gt;otherenv&lt;/code&gt; environment by running &lt;code&gt;conda activate otherenv&lt;/code&gt;. You&amp;rsquo;ll notice the environment has changed by the indication between parentheses in the beginning of the prompt:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda activate otherenv

&lt;span class=&quot;gp&quot;&gt;(otherenv) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can open the Python interpreter within this environment by running &lt;code&gt;python&lt;/code&gt;:&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;(otherenv) C:\Users\IEUser&amp;gt;&lt;/span&gt;python
&lt;span class=&quot;go&quot;&gt;Python 3.7.0 (default, Jun 28 2018, 08:04:48) [MSC v.1912 64 bit (AMD64)] :: Anaconda, Inc. on win32&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Type &amp;quot;help&amp;quot;, &amp;quot;copyright&amp;quot;, &amp;quot;credits&amp;quot; or &amp;quot;license&amp;quot; for more information.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The environment includes Python 3.7.0, the same version included in the root base environment. To exit the Python interpreter, just run &lt;code&gt;quit()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight doscon&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;&amp;gt;&amp;gt;&amp;gt; quit()&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;(otherenv) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To deactivate the &lt;code&gt;otherenv&lt;/code&gt; environment and go back to the root base environment, you should run &lt;code&gt;deactivate&lt;/code&gt;:&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;(otherenv) C:\Users\IEUser&amp;gt;&lt;/span&gt;deactivate

&lt;span class=&quot;gp&quot;&gt;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As mentioned earlier, Conda allows you to easily create environments with different versions of Python, which is not straightforward with Virtualenv. To include a different Python version within an environment, you have to specify it by using &lt;code&gt;python=&amp;lt;version&amp;gt;&lt;/code&gt; when running &lt;code&gt;conda create&lt;/code&gt;. For example, to create an environment named &lt;code&gt;py2&lt;/code&gt; with Python 2.7, you have to run &lt;code&gt;conda create --name py2 python=2.7&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda create --name py2 python=2.7
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3\envs\py2&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  added / updated specs:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - python=2.7&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    certifi:        2018.8.24-py27_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pip:            10.0.1-py27_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    python:         2.7.15-he216670_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    setuptools:     40.2.0-py27_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vc:             9-h7299396_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vs2008_runtime: 9.00.30729.1-hfaea7d5_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wheel:          0.31.1-py27_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wincertstore:   0.2-py27hf04cefb_0&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)? y&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Preparing transaction: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Verifying transaction: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Executing transaction: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# To activate this environment, use&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#     $ conda activate py2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# To deactivate an active environment, use&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#     $ conda deactivate&lt;/span&gt;


&lt;span class=&quot;gp&quot;&gt;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As shown by the output of &lt;code&gt;conda create&lt;/code&gt;, this time some new packages were installed, since the new environment uses Python 2. You can check the new environment indeed uses Python 2 by activating it and running the Python interpreter:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda activate py2

&lt;span class=&quot;gp&quot;&gt;(py2) C:\Users\IEUser&amp;gt;&lt;/span&gt;python
&lt;span class=&quot;go&quot;&gt;Python 2.7.15 |Anaconda, Inc.| (default, May  1 2018, 18:37:09) [MSC v.1500 64 bit (AMD64)] on win32&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Type &amp;quot;help&amp;quot;, &amp;quot;copyright&amp;quot;, &amp;quot;credits&amp;quot; or &amp;quot;license&amp;quot; for more information.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, if you run &lt;code&gt;conda env list&lt;/code&gt;, you should see the two environments that were created, besides the root base environment:&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;(py2) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda env list
&lt;span class=&quot;go&quot;&gt;# conda environments:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;base                     C:\Users\IEUser\Miniconda3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;otherenv                 C:\Users\IEUser\Miniconda3\envs\otherenv&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;py2               *  C:\Users\IEUser\Miniconda3\envs\py2&lt;/span&gt;


&lt;span class=&quot;gp&quot;&gt;(py2) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the list, the asterisk indicates the activated environment. It is possible to remove an environment by running &lt;code&gt;conda remove --name &amp;lt;environment name&amp;gt; --all&lt;/code&gt;. Since it is not possible to remove an activated environment, you should first deactivate the &lt;code&gt;py2&lt;/code&gt; environment, to remove it:&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;(py2) C:\Users\IEUser&amp;gt;&lt;/span&gt;deactivate

&lt;span class=&quot;gp&quot;&gt;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda remove --name py2 --all

&lt;span class=&quot;go&quot;&gt;Remove all packages in environment C:\Users\IEUser\Miniconda3\envs\py2:&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3\envs\py2&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;The following packages will be REMOVED:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    certifi:        2018.8.24-py27_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pip:            10.0.1-py27_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    python:         2.7.15-he216670_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    setuptools:     40.2.0-py27_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vc:             9-h7299396_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vs2008_runtime: 9.00.30729.1-hfaea7d5_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wheel:          0.31.1-py27_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wincertstore:   0.2-py27hf04cefb_0&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)? y&lt;/span&gt;


&lt;span class=&quot;gp&quot;&gt;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that you&amp;rsquo;ve covered the basics of managing environments with Conda, let&amp;rsquo;s see how to manage packages within the environments.&lt;/p&gt;
&lt;h2 id=&quot;understanding-basic-package-management-with-conda&quot;&gt;Understanding Basic Package Management With Conda&lt;/h2&gt;
&lt;p&gt;Within each environment, packages of software can be installed using the Conda package manager. The root base environment created by the Miniconda installer includes some packages by default that are not part of Python standard library.&lt;/p&gt;
&lt;p&gt;The default installation includes the minimum packages necessary to use Conda. To check the list of installed packages in an environment, you just have to make sure it is activated and run &lt;code&gt;conda list&lt;/code&gt;. In the root environment, the following packages are installed by default:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda list
&lt;span class=&quot;go&quot;&gt;# packages in environment at C:\Users\IEUser\Miniconda3:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# Name                    Version                   Build  Channel&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;asn1crypto                0.24.0                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ca-certificates           2018.03.07                    0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;certifi                   2018.8.24                py37_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;cffi                      1.11.5           py37h74b6da3_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;chardet                   3.0.4                    py37_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;conda                     4.5.11                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;conda-env                 2.6.0                         1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;console_shortcut          0.1.1                         3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;cryptography              2.3.1            py37h74b6da3_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;idna                      2.7                      py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;menuinst                  1.4.14           py37hfa6e2cd_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;openssl                   1.0.2p               hfa6e2cd_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pip                       10.0.1                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pycosat                   0.6.3            py37hfa6e2cd_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pycparser                 2.18                     py37_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pyopenssl                 18.0.0                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pysocks                   1.6.8                    py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;python                    3.7.0                hea74fb7_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pywin32                   223              py37hfa6e2cd_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;requests                  2.19.1                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ruamel_yaml               0.15.46          py37hfa6e2cd_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;setuptools                40.2.0                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;six                       1.11.0                   py37_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;urllib3                   1.23                     py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;vc                        14                   h0510ff6_3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;vs2015_runtime            14.0.25123                    3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;wheel                     0.31.1                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;win_inet_pton             1.0.1                    py37_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;wincertstore              0.2                      py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;yaml                      0.1.7                hc54c509_2&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To manage the packages, you should also use Conda. Next, let&amp;rsquo;s see how to search, install, update, and remove packages using Conda.&lt;/p&gt;
&lt;h3 id=&quot;searching-and-installing-packages&quot;&gt;Searching and Installing Packages&lt;/h3&gt;
&lt;p&gt;Packages are installed from repositories called &lt;strong&gt;channels&lt;/strong&gt; by Conda, and some default channels are configured by the installer. To search for a specific package, you can run &lt;code&gt;conda search &amp;lt;package name&amp;gt;&lt;/code&gt;. For example, this is how you search for the &lt;code&gt;keras&lt;/code&gt; package (a machine learning library):&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda search keras
&lt;span class=&quot;go&quot;&gt;Loading channels: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# Name                  Version           Build  Channel&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.0.8  py35h15001cb_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.0.8  py36h65e7a35_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.1.2          py35_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.1.2          py36_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.1.3          py35_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.1.3          py36_0  pkgs/main&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;... (more)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;According to the previous output, there are different versions of the package and different builds for each version, such as for Python 3.5 and 3.6.&lt;/p&gt;
&lt;p&gt;The previous search shows only exact matches for packages named &lt;code&gt;keras&lt;/code&gt;. To perform a broader search, including all packages containing &lt;code&gt;keras&lt;/code&gt; in their names, you should use the wildcard &lt;code&gt;*&lt;/code&gt;. For example, when you run &lt;code&gt;conda search *keras*&lt;/code&gt;, you get the following:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda search *keras*
&lt;span class=&quot;go&quot;&gt;Loading channels: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# Name                  Version           Build  Channel&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.0.8  py35h15001cb_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.0.8  py36h65e7a35_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.1.2          py35_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.1.2          py36_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.1.3          py35_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras                     2.1.3          py36_0  pkgs/main&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;... (more)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;keras-applications           1.0.2          py35_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras-applications           1.0.2          py36_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras-applications           1.0.4          py35_0  pkgs/main&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;... (more)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;keras-base                2.2.0          py35_0  pkgs/main&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;keras-base                2.2.0          py36_0  pkgs/main&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;... (more)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As the previous output shows, there are some other keras related packages in the default channels.&lt;/p&gt;
&lt;p&gt;To install a package, you should run &lt;code&gt;conda install &amp;lt;package name&amp;gt;&lt;/code&gt;. By default, the newest version of the package will be installed in the active environment. So, let&amp;rsquo;s install the package &lt;code&gt;keras&lt;/code&gt; in the environment &lt;code&gt;otherenv&lt;/code&gt; that you&amp;rsquo;ve already created:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda activate otherenv

&lt;span class=&quot;gp&quot;&gt;(otherenv) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda install keras
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3\envs\otherenv&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  added / updated specs:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - keras&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    _tflow_1100_select:  0.0.3-mkl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    absl-py:             0.4.1-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    astor:               0.7.1-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    blas:                1.0-mkl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    certifi:             2018.8.24-py36_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    gast:                0.2.0-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    grpcio:              1.12.1-py36h1a1b453_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    h5py:                2.8.0-py36h3bdd7fb_2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    hdf5:                1.10.2-hac2f561_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    icc_rt:              2017.0.4-h97af966_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    intel-openmp:        2018.0.3-0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    keras:               2.2.2-0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    keras-applications:  1.0.4-py36_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    keras-base:          2.2.2-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    keras-preprocessing: 1.0.2-py36_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    libmklml:            2018.0.3-1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    libprotobuf:         3.6.0-h1a1b453_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    markdown:            2.6.11-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl:                 2019.0-117&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl_fft:             1.0.4-py36h1e22a9b_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl_random:          1.0.1-py36h77b88f5_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    numpy:               1.15.1-py36ha559c80_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    numpy-base:          1.15.1-py36h8128ebf_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pip:                 10.0.1-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    protobuf:            3.6.0-py36he025d50_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    python:              3.6.6-hea74fb7_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pyyaml:              3.13-py36hfa6e2cd_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    scipy:               1.1.0-py36h4f6bf74_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    setuptools:          40.2.0-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    six:                 1.11.0-py36_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    tensorboard:         1.10.0-py36he025d50_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    tensorflow:          1.10.0-mkl_py36hb361250_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    tensorflow-base:     1.10.0-mkl_py36h81393da_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    termcolor:           1.1.0-py36_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vc:                  14-h0510ff6_3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vs2013_runtime:      12.0.21005-1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vs2015_runtime:      14.0.25123-3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    werkzeug:            0.14.1-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wheel:               0.31.1-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wincertstore:        0.2-py36h7fe50ca_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    yaml:                0.1.7-hc54c509_2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    zlib:                1.2.11-h8395fce_2&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Conda manages the necessary dependencies for a package when it is installed. Since the package &lt;code&gt;keras&lt;/code&gt; has a lot of dependencies, when you install it, Conda manages to install this big list of packages.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth noticing that, since the &lt;code&gt;keras&lt;/code&gt; package&amp;rsquo;s newest build uses Python 3.6 and the &lt;code&gt;otherenv&lt;/code&gt; environment was created using Python 3.7, the package &lt;code&gt;python&lt;/code&gt; version 3.6.6 was included as a dependency. After confirming the installation, you can check that the Python version for the &lt;code&gt;otherenv&lt;/code&gt; environment is downgraded to the 3.6.6 version.&lt;/p&gt;
&lt;p&gt;Sometimes, you don&amp;rsquo;t want packages to be downgraded, and it would be better to just create a new environment with the necessary version of Python. To check the list of new packages, updates, and downgrades necessary for a package without installing it, you should use the parameter &lt;code&gt;--dry-run&lt;/code&gt;. For example, to check the packages that will be changed by the installation of the package &lt;code&gt;keras&lt;/code&gt;, you should run the following:&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;(otherenv) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda install keras --dry-run
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, if necessary, it is possible to change the default Python of a Conda environment by installing a specific version of the package &lt;code&gt;python&lt;/code&gt;. To demonstrate that, let&amp;rsquo;s create a new environment called &lt;code&gt;envpython&lt;/code&gt;:&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;(otherenv) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda create --name envpython
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3\envs\envpython&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)? y&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Preparing transaction: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Verifying transaction: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Executing transaction: done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# To activate this environment, use&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#     $ conda activate envpython&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# To deactivate an active environment, use&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#     $ conda deactivate&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you saw before, since the root base environment uses Python 3.7, &lt;code&gt;envpython&lt;/code&gt; is created including this same version of Python:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda activate envpython

&lt;span class=&quot;gp&quot;&gt;(envpython) C:\Users\IEUser&amp;gt;&lt;/span&gt;python
&lt;span class=&quot;go&quot;&gt;Python 3.7.0 (default, Jun 28 2018, 08:04:48) [MSC v.1912 64 bit (AMD64)] :: Anaconda, Inc. on win32&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Type &amp;quot;help&amp;quot;, &amp;quot;copyright&amp;quot;, &amp;quot;credits&amp;quot; or &amp;quot;license&amp;quot; for more information.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;gt;&amp;gt;&amp;gt; quit()&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;(envpython) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To install a specific version of a package, you can run &lt;code&gt;conda install &amp;lt;package name&amp;gt;=&amp;lt;version&amp;gt;&lt;/code&gt;. For example, this is how you install Python 3.6 in the &lt;code&gt;envpython&lt;/code&gt; environment:&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;(envpython) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda install python=3.6
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3\envs\envpython&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  added / updated specs:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - python=3.6&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    certifi:        2018.8.24-py36_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pip:            10.0.1-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    python:         3.6.6-hea74fb7_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    setuptools:     40.2.0-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vc:             14-h0510ff6_3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vs2015_runtime: 14.0.25123-3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wheel:          0.31.1-py36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wincertstore:   0.2-py36h7fe50ca_0&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In case you need to install more than one package in an environment, it is possible to run &lt;code&gt;conda install&lt;/code&gt; only once, passing the names of the packages. To illustrate that, let&amp;rsquo;s install &lt;code&gt;numpy&lt;/code&gt;, &lt;code&gt;scipy&lt;/code&gt;, and &lt;code&gt;matplotlib&lt;/code&gt;, basic packages for numerical computation in the root base environment:&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;(envpython) C:\Users\IEUser&amp;gt;&lt;/span&gt;deactivate

&lt;span class=&quot;gp&quot;&gt;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda install numpy scipy matplotlib
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  added / updated specs:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - matplotlib&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - numpy&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - scipy&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;The following packages will be downloaded:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    package                    |            build&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    ---------------------------|-----------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    libpng-1.6.34              |       h79bbb47_0         1.3 MB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl_random-1.0.1           |   py37h77b88f5_1         267 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    intel-openmp-2019.0        |              117         1.7 MB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    qt-5.9.6                   |   vc14h62aca36_0        92.5 MB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    matplotlib-2.2.3           |   py37hd159220_0         6.5 MB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    tornado-5.1                |   py37hfa6e2cd_0         668 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pyqt-5.9.2                 |   py37ha878b3d_0         4.6 MB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pytz-2018.5                |           py37_0         232 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    scipy-1.1.0                |   py37h4f6bf74_1        13.5 MB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    jpeg-9b                    |       hb83a4c4_2         313 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    python-dateutil-2.7.3      |           py37_0         260 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    numpy-base-1.15.1          |   py37h8128ebf_0         3.9 MB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    numpy-1.15.1               |   py37ha559c80_0          37 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl_fft-1.0.4              |   py37h1e22a9b_1         120 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    kiwisolver-1.0.1           |   py37h6538335_0          61 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pyparsing-2.2.0            |           py37_1          96 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    cycler-0.10.0              |           py37_0          13 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    freetype-2.9.1             |       ha9979f8_1         470 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    icu-58.2                   |       ha66f8fd_1        21.9 MB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    sqlite-3.24.0              |       h7602738_0         899 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    sip-4.19.12                |   py37h6538335_0         283 KB&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    ------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                                           Total:       149.5 MB&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    blas:            1.0-mkl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    cycler:          0.10.0-py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    freetype:        2.9.1-ha9979f8_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    icc_rt:          2017.0.4-h97af966_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    icu:             58.2-ha66f8fd_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    intel-openmp:    2019.0-117&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    jpeg:            9b-hb83a4c4_2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    kiwisolver:      1.0.1-py37h6538335_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    libpng:          1.6.34-h79bbb47_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    matplotlib:      2.2.3-py37hd159220_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl:             2019.0-117&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl_fft:         1.0.4-py37h1e22a9b_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl_random:      1.0.1-py37h77b88f5_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    numpy:           1.15.1-py37ha559c80_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    numpy-base:      1.15.1-py37h8128ebf_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pyparsing:       2.2.0-py37_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pyqt:            5.9.2-py37ha878b3d_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    python-dateutil: 2.7.3-py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pytz:            2018.5-py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    qt:              5.9.6-vc14h62aca36_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    scipy:           1.1.0-py37h4f6bf74_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    sip:             4.19.12-py37h6538335_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    sqlite:          3.24.0-h7602738_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    tornado:         5.1-py37hfa6e2cd_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    zlib:            1.2.11-h8395fce_2&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that you&amp;rsquo;ve covered how to search and install packages, let&amp;rsquo;s see how to update and remove them using Conda.&lt;/p&gt;
&lt;h3 id=&quot;updating-and-removing-packages&quot;&gt;Updating and Removing Packages&lt;/h3&gt;
&lt;p&gt;Sometimes, when new packages are released, you need to update them. To do so, you may run &lt;code&gt;conda update &amp;lt;package name&amp;gt;&lt;/code&gt;. In case you wish to update all the packages within one environment, you should activate the environment and run &lt;code&gt;conda update --all&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To remove a package, you can run &lt;code&gt;conda remove &amp;lt;package name&amp;gt;&lt;/code&gt;. For example, this is how you remove &lt;code&gt;numpy&lt;/code&gt; from the root base environment:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda remove numpy
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  removed specs:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - numpy&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;The following packages will be REMOVED:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    matplotlib: 2.2.3-py37hd159220_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl_fft:    1.0.4-py37h1e22a9b_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    mkl_random: 1.0.1-py37h77b88f5_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    numpy:      1.15.1-py37ha559c80_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    scipy:      1.1.0-py37h4f6bf74_1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It&amp;rsquo;s worth noting that when you remove a package, all packages that depend on it are also removed.&lt;/p&gt;
&lt;h3 id=&quot;using-channels&quot;&gt;Using Channels&lt;/h3&gt;
&lt;p&gt;Sometimes, you won&amp;rsquo;t find the packages you want to install on the default channels configured by the installer. For example, this is how you install &lt;code&gt;pytorch&lt;/code&gt;, another machine learning package:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda search pytorch
&lt;span class=&quot;go&quot;&gt;Loading channels: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;PackagesNotFoundError: The following packages are not available from current channels:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  - pytorch&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Current channels:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/main/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/main/noarch&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/free/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/free/noarch&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/r/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/r/noarch&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/pro/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/pro/noarch&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/msys2/win-64&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  - https://repo.anaconda.com/pkgs/msys2/noarch&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;To search for alternate channels that may provide the conda package you&amp;#39;re&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;looking for, navigate to&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    https://anaconda.org&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;and use the search bar at the top of the page.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, you may search for the package &lt;a href=&quot;https://anaconda.org/search&quot;&gt;here&lt;/a&gt;. If you search for &lt;code&gt;pytorch&lt;/code&gt;, you&amp;rsquo;ll get the following results:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pytorch_anaconda_search.dcb73b6d552d.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/pytorch_anaconda_search.dcb73b6d552d.png&quot; width=&quot;1520&quot; height=&quot;936&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pytorch_anaconda_search.dcb73b6d552d.png&amp;amp;w=380&amp;amp;sig=eb51f4b976784464316e6a0129ce677f4ecafab7 380w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pytorch_anaconda_search.dcb73b6d552d.png&amp;amp;w=760&amp;amp;sig=fb2658e66bb7fa6d5de810adb67c5754c375e1d8 760w, https://files.realpython.com/media/pytorch_anaconda_search.dcb73b6d552d.png 1520w&quot; sizes=&quot;75vw&quot; alt=&quot;Anaconda Search for pytorch&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The channel &lt;code&gt;pytorch&lt;/code&gt; has a package named &lt;code&gt;pytorch&lt;/code&gt; with version &lt;code&gt;0.4.1&lt;/code&gt;. To install a package from a specific channel you can use the &lt;code&gt;-c &amp;lt;channel&amp;gt;&lt;/code&gt; parameter with &lt;code&gt;conda install&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda install -c pytorch pytorch
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  added / updated specs:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - pytorch&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;The following packages will be downloaded:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    package                    |            build&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    ---------------------------|-----------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pytorch-0.4.1              |py37_cuda90_cudnn7he774522_1       590.4 MB  pytorch&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    pytorch: 0.4.1-py37_cuda90_cudnn7he774522_1 pytorch&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, you can add the channel, so that Conda uses it to search for packages to install. To list the current channels used, you can run &lt;code&gt;conda config --get channels&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda config --get channels
&lt;span class=&quot;go&quot;&gt;--add channels &amp;#39;defaults&amp;#39;   # lowest priority&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The Miniconda installer includes only the &lt;code&gt;defaults&lt;/code&gt; channels. When more channels are included, it is necessary to set the priority of them to determine from which channel a package will be installed in case it is available from more than one channel.&lt;/p&gt;
&lt;p&gt;To add a channel with the lowest priority to the list, you should run &lt;code&gt;conda config --append channels &amp;lt;channel name&amp;gt;&lt;/code&gt;. To add a channel with the highest priority to the list, you should run &lt;code&gt;conda config --prepend channels &amp;lt;channel name&amp;gt;&lt;/code&gt;. It is recommended to add new channels with low priority, to keep using the default channels prior to the others. So, alternatively, you can  install &lt;code&gt;pytorch&lt;/code&gt;, adding the &lt;code&gt;pytorch&lt;/code&gt; channel and running &lt;code&gt;conda install pytorch&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda config --append channels pytorch

&lt;span class=&quot;gp&quot;&gt;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda config --get channels
&lt;span class=&quot;go&quot;&gt;--add channels &amp;#39;pytorch&amp;#39;   # lowest priority&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--add channels &amp;#39;defaults&amp;#39;   # highest priority&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda install pytorch
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  added / updated specs:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - pytorch&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;The following packages will be downloaded:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    package                    |            build&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    ---------------------------|-----------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pytorch-0.4.1              |py37_cuda90_cudnn7he774522_1       590.4 MB  pytorch&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    pytorch: 0.4.1-py37_cuda90_cudnn7he774522_1 pytorch&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Not all packages are available on Conda channels. However, this is not a problem, since you also can use &lt;code&gt;pip&lt;/code&gt; to install packages inside Conda environments. Let&amp;rsquo;s see how to do this.&lt;/p&gt;
&lt;h3 id=&quot;using-pip-inside-conda-environments&quot;&gt;Using &lt;code&gt;pip&lt;/code&gt; Inside Conda Environments&lt;/h3&gt;
&lt;p&gt;Sometimes, you may need pure Python packages and, generally, these packages are not available on Conda&amp;rsquo;s channels. For example, if you search for &lt;code&gt;unipath&lt;/code&gt;, a package to deal with file paths in Python, Conda won&amp;rsquo;t be able to find it.&lt;/p&gt;
&lt;p&gt;You could search for the package &lt;a href=&quot;https://anaconda.org/search&quot;&gt;here&lt;/a&gt; and use another channel to install it. However, since &lt;code&gt;unipath&lt;/code&gt; is a pure Python package, you could use &lt;code&gt;pip&lt;/code&gt; to install it, as you would do on a regular Python setup. The only difference is that you should use &lt;code&gt;pip&lt;/code&gt; installed by the Conda package &lt;code&gt;pip&lt;/code&gt;. To illustrate that, let&amp;rsquo;s create a new environment called &lt;code&gt;newproject&lt;/code&gt;. As mentioned before, you can do this running &lt;code&gt;conda create&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight doscon&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;conda create --name newproject&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, to have &lt;code&gt;pip&lt;/code&gt; installed, you should activate the environment and install the Conda package &lt;code&gt;pip&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda activate newproject

&lt;span class=&quot;gp&quot;&gt;(newproject) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda install pip
&lt;span class=&quot;go&quot;&gt;Solving environment: done&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;## Package Plan ##&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  environment location: C:\Users\IEUser\Miniconda3\envs\newproject&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;  added / updated specs:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - pip&lt;/span&gt;


&lt;span class=&quot;go&quot;&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    certifi:        2018.8.24-py37_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    pip:            10.0.1-py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    python:         3.7.0-hea74fb7_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    setuptools:     40.2.0-py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vc:             14-h0510ff6_3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vs2015_runtime: 14.0.25123-3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wheel:          0.31.1-py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    wincertstore:   0.2-py37_0&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Proceed ([y]/n)?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, use &lt;code&gt;pip&lt;/code&gt; to install the package &lt;code&gt;unipath&lt;/code&gt;:&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;(newproject) C:\Users\IEUser&amp;gt;&lt;/span&gt;pip install unipath
&lt;span class=&quot;go&quot;&gt;Collecting unipath&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installing collected packages: unipath&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Successfully installed unipath-1.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;You are using pip version 10.0.1, however version 18.0 is available.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;You should consider upgrading via the &amp;#39;python -m pip install --upgrade pip&amp;#39; command.&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;(newproject) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After installation, you can list the installed packages with &lt;code&gt;conda list&lt;/code&gt; and check that &lt;code&gt;Unipath&lt;/code&gt; was installed using pip:&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;(newproject) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda list
&lt;span class=&quot;go&quot;&gt;# packages in environment at C:\Users\IEUser\Miniconda3\envs\newproject:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# Name                    Version                   Build  Channel&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;certifi                   2018.8.24                py37_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pip                       10.0.1                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;python                    3.7.0                hea74fb7_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;setuptools                40.2.0                   py37_0&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;Unipath                   1.1                       &amp;lt;pip&amp;gt;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;vc                        14                   h0510ff6_3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;vs2015_runtime            14.0.25123                    3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;wheel                     0.31.1                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;wincertstore              0.2                      py37_0&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;(newproject) C:\Users\IEUser&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It&amp;rsquo;s also possible to install packages from a version control system (VCS) using &lt;code&gt;pip&lt;/code&gt;. For example, let&amp;rsquo;s install &lt;code&gt;supervisor&lt;/code&gt;, version 4.0.0dev0, available in a Git repository. As Git is not installed in the &lt;code&gt;newproject&lt;/code&gt; environment, you should install it first:&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;(newproject) C:\Users\IEUser&amp;gt;&lt;/span&gt; conda install git
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, install &lt;code&gt;supervisor&lt;/code&gt;, using &lt;code&gt;pip&lt;/code&gt; to install it from the Git repository:&lt;/p&gt;
&lt;div class=&quot;highlight doscon&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;(newproject) pip install -e git://github.com/Supervisor/supervisor@abef0a2be35f4aae4a4edeceadb7a213b729ef8d#egg=supervisor&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After the installation finishes, you can see that &lt;code&gt;supervisor&lt;/code&gt; is listed in the installed packages list:&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;(newproject) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda list
&lt;span class=&quot;go&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;# Name                    Version                   Build  Channel&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;certifi                   2018.8.24                py37_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;git                       2.18.0               h6bb4b03_0&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;meld3                     1.0.2                     &amp;lt;pip&amp;gt;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pip                       10.0.1                   py37_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;python                    3.7.0                hea74fb7_0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;setuptools                40.2.0                   py37_0&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;supervisor                4.0.0.dev0                &amp;lt;pip&amp;gt;&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;... (more)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that you know the basics of using environments and managing packages with Conda, let&amp;rsquo;s create a simple machine learning example to solve a classic problem using a neural network.&lt;/p&gt;
&lt;h2 id=&quot;a-simple-machine-learning-example&quot;&gt;A Simple Machine Learning Example&lt;/h2&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll set up the environment using Conda and train a neural network to function like an &lt;a href=&quot;https://en.wikipedia.org/wiki/XOR_gate&quot;&gt;XOR&lt;/a&gt; gate.&lt;/p&gt;
&lt;p&gt;An XOR gate implements the digital logic &lt;a href=&quot;https://en.wikipedia.org/wiki/Exclusive_or&quot;&gt;exclusive OR operation&lt;/a&gt;, which is widely used in digital systems. It takes two digital inputs, that can be equal to &lt;code&gt;0&lt;/code&gt;, representing a digital &lt;strong&gt;false&lt;/strong&gt; value or &lt;code&gt;1&lt;/code&gt;, representing a digital &lt;strong&gt;true&lt;/strong&gt; value and outputs &lt;code&gt;1&lt;/code&gt; (&lt;strong&gt;true&lt;/strong&gt;) if the inputs are different or &lt;code&gt;0&lt;/code&gt; (&lt;strong&gt;false&lt;/strong&gt;), if the inputs are equal. The following table (referred as a &lt;strong&gt;truth table&lt;/strong&gt; in the digital systems terminology) summarizes the XOR gate operation:&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;Input A&lt;/th&gt;
&lt;th&gt;Input B&lt;/th&gt;
&lt;th&gt;Output: A XOR B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The XOR operation can be interpreted as a classification problem, given that it takes two inputs and should classify them in one of two classes represented by &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;, depending on whether the inputs are equal to each other or different from one another.&lt;/p&gt;
&lt;p&gt;It is commonly used as a first example to train a neural network because it is simple and, at the same time, demands a nonlinear classifier, such as a neural network. The neural network will use only the data from the truth table, without knowledge about where it came from, to &amp;ldquo;learn&amp;rdquo; the operation performed by the XOR gate.&lt;/p&gt;
&lt;p&gt;To implement the neural network, let&amp;rsquo;s create a new Conda environment, named &lt;code&gt;nnxor&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda create nnxor
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, let&amp;rsquo;s activate it and install the package &lt;code&gt;keras&lt;/code&gt;:&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;(base) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda activate nnxor

&lt;span class=&quot;gp&quot;&gt;(nnxor) C:\Users\IEUser&amp;gt;&lt;/span&gt;conda install keras
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://keras.io/&quot;&gt;&lt;code&gt;keras&lt;/code&gt;&lt;/a&gt; is a high-level API that makes easy-to-implement neural networks on top of well-known machine learning libraries, such as &lt;a href=&quot;https://www.tensorflow.org/&quot;&gt;TensorFlow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll train the following neural network to act as an XOR gate:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/nnxor.a334b7848a9b.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/nnxor.a334b7848a9b.png&quot; width=&quot;1024&quot; height=&quot;363&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/nnxor.a334b7848a9b.png&amp;amp;w=256&amp;amp;sig=11f07b0d04b5b9745e6447caa55989b54771c13e 256w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/nnxor.a334b7848a9b.png&amp;amp;w=512&amp;amp;sig=25b9e3bdc3c7b8c1278c444fa6f86b407dd88287 512w, https://files.realpython.com/media/nnxor.a334b7848a9b.png 1024w&quot; sizes=&quot;75vw&quot; alt=&quot;XOR gate neural network&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The network takes two inputs, &lt;em&gt;A&lt;/em&gt; and &lt;em&gt;B&lt;/em&gt;, and feeds them to two neurons, represented by the big circles. Then, it takes the outputs of these two neurons and feeds them to an output neuron, which should provide the classification according to the XOR truth table.&lt;/p&gt;
&lt;p&gt;In brief, the training process consists of adjusting the values of the weights &lt;em&gt;w_1&lt;/em&gt; until &lt;em&gt;w_6&lt;/em&gt;, so that the output is consistent with the XOR truth table. To do so, input examples will be fed, one at a time, the output will be calculated according to current values of the weights and, by comparing the output with the desired output, given by the truth table, the values of the weights will be adjusted in a step-by-step process.&lt;/p&gt;
&lt;p&gt;To organize the project, you&amp;rsquo;ll create a folder named &lt;code&gt;nnxor&lt;/code&gt; within Windows user&amp;rsquo;s folder (&lt;code&gt;C:\Users\IEUser&lt;/code&gt;) with a file named &lt;code&gt;nnxor.py&lt;/code&gt; to store the Python program to implement the neural network:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/nnxor_file.799d361d9286.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/nnxor_file.799d361d9286.png&quot; width=&quot;1179&quot; height=&quot;345&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/nnxor_file.799d361d9286.png&amp;amp;w=294&amp;amp;sig=79e1388ae7fb2318f51d3b4b622e67a6e6d463bb 294w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/nnxor_file.799d361d9286.png&amp;amp;w=589&amp;amp;sig=6484c6ebf74b160f1ac9c8d1fa459d3dbc4b0290 589w, https://files.realpython.com/media/nnxor_file.799d361d9286.png 1179w&quot; sizes=&quot;75vw&quot; alt=&quot;Program File&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;nnxor.py&lt;/code&gt; file, you&amp;rsquo;ll define the network, perform the training, and test 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;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;n&quot;&gt;np&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;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;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;keras.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;keras.layers.core&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dense&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Activation&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;keras.optimizers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SGD&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;array&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;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
              &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;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;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;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;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;n&quot;&gt;y&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;array&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;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;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;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;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_dim&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;model&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;Activation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sigmoid&amp;#39;&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;model&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;Activation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sigmoid&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sgd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SGD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lr&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;model&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;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;mean_squared_error&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sgd&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;fit&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;n&quot;&gt;batch_size&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;epochs&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;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&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;predict&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;First, you import &lt;code&gt;numpy&lt;/code&gt;, initialize a random seed, so that you can reproduce the same results when running the program again, and import the &lt;code&gt;keras&lt;/code&gt; objects you&amp;rsquo;ll use to build the neural network.&lt;/p&gt;
&lt;p&gt;Then, you define an &lt;code&gt;X&lt;/code&gt; array, containing the 4 possible A-B sets of inputs for the XOR operation and a &lt;code&gt;y&lt;/code&gt; array, containing the outputs for each of the sets of inputs defined in &lt;code&gt;X&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The next five lines define the neural network. The &lt;code&gt;Sequential()&lt;/code&gt; model is one of the models provided by &lt;code&gt;keras&lt;/code&gt; to define a neural network, in which the layers of the network are defined in a sequential way. Then you define the first layer of neurons, composed of two neurons, fed by two inputs, defining their activation function as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Sigmoid_function&quot;&gt;sigmoid function&lt;/a&gt; in the sequence. Finally, you define the output layer composed of one neuron with the same activation function.&lt;/p&gt;
&lt;p&gt;The following two lines define the details about the training of the network. To adjust the weights of the network, you&amp;rsquo;ll use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Stochastic_gradient_descent&quot;&gt;Stochastic Gradient Descent&lt;/a&gt; (SGD) with the learning rate equal to &lt;code&gt;0.1&lt;/code&gt;, and you&amp;rsquo;ll use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Mean_squared_error&quot;&gt;mean squared error&lt;/a&gt; as a loss function to be minimized.&lt;/p&gt;
&lt;p&gt;Finally, you perform the training by running the &lt;code&gt;fit()&lt;/code&gt; method, using &lt;code&gt;X&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; as training examples and updating the weights after every training example is fed into the network (&lt;code&gt;batch_size=1&lt;/code&gt;). The number of &lt;code&gt;epochs&lt;/code&gt; represents the number of times the whole training set will be used to train the neural network.&lt;/p&gt;
&lt;p&gt;In this case, you&amp;rsquo;re repeating the training 5000 times using a training set containing 4 input-output examples. By default, each time the training set is used, the training examples are shuffled.&lt;/p&gt;
&lt;p&gt;On the last line, after the training process has finished, you print the predicted values for the 4 possible input examples.&lt;/p&gt;
&lt;p&gt;By running this script, you&amp;rsquo;ll see the evolution of the training process and the performance improvement as new training examples are fed into the network:&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;(nnxor) C:\Users\IEUser&amp;gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cd&lt;/span&gt; nnxor

&lt;span class=&quot;gp&quot;&gt;(nnxor) C:\Users\IEUser\nnxor&amp;gt;&lt;/span&gt;python nnxor.py
&lt;span class=&quot;go&quot;&gt;Using TensorFlow backend.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Epoch 1/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2018-09-16 09:49:05.987096: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2018-09-16 09:49:05.993128: I tensorflow/core/common_runtime/process_util.cc:69] Creating new thread pool with default inter op setting: 2. Tune using inter_op_parallelism_threads for best performance.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 39ms/step - loss: 0.2565&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Epoch 2/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 0us/step - loss: 0.2566&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Epoch 3/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 0us/step - loss: 0.2566&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Epoch 4/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 0us/step - loss: 0.2566&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Epoch 5/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 0us/step - loss: 0.2566&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Epoch 6/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 0us/step - loss: 0.2566&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After the training finishes, you can check the predictions the network gives for the possible input values:&lt;/p&gt;
&lt;div class=&quot;highlight doscon&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;Epoch 4997/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 0us/step - loss: 0.0034&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Epoch 4998/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 0us/step - loss: 0.0034&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Epoch 4999/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 0us/step - loss: 0.0034&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Epoch 5000/5000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4/4 [==============================] - 0s 0us/step - loss: 0.0034&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[[0.0587215 ]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [0.9468337 ]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [0.9323144 ]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [0.05158457]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you defined &lt;code&gt;X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])&lt;/code&gt;, the expected output values are &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, and &lt;code&gt;0&lt;/code&gt;, which is consistent with the predicted outputs of the network, given you should round them to obtain binary values.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-from-here&quot;&gt;Where To Go From Here&lt;/h2&gt;
&lt;p&gt;Data science and machine learning applications are emerging in the most diverse areas, attracting more people. However, setting up an environment for numerical computation can be a complicated task, and it&amp;rsquo;s common to find users having trouble in data science workshops, especially when using Windows.&lt;/p&gt;
&lt;p&gt;In this article, you&amp;rsquo;ve covered the basics of setting up a Python numerical computation environment on a Windows machine using the Anaconda Python distribution.&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;https://realpython.com/optins/view/conda-cheatsheet/&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-conda-cheatsheet&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a Conda cheat sheet&lt;/a&gt; with handy usage examples for managing your Python environment and packages.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Now that you have a working environment, it&amp;rsquo;s time to start working with some applications. Python is one of the most used languages for data science and machine learning, and Anaconda is one of the most popular distributions, used in various companies and research laboratories. It provides several packages to install libraries that Python relies on for data acquisition, wrangling, processing, and visualization.&lt;/p&gt;
&lt;p&gt;Fortunately there are a lot of &lt;a href=&quot;https://realpython.com/tutorials/data-science/&quot;&gt;tutorials about these libraries&lt;/a&gt; available at Real Python, including the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/tutorials/numpy/&quot;&gt;NumPy tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-matplotlib-guide/&quot;&gt;Python Plotting With Matplotlib (Guide)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-histograms/&quot;&gt;Python Histogram Plotting: NumPy, Matplotlib, Pandas &amp;amp; Seaborn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/numpy-tensorflow-performance/&quot;&gt;Pure Python vs NumPy vs TensorFlow Performance Comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-pandas-tricks/&quot;&gt;Python Pandas: Tricks &amp;amp; Features You May Not Know&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/fast-flexible-pandas/&quot;&gt;Fast, Flexible, Easy, and Intuitive: How to Speed Up Your Pandas Projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-data-cleaning-numpy-pandas/&quot;&gt;Pythonic Data Cleaning With NumPy and Pandas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, if you&amp;rsquo;d like a deeper understanding of Anaconda and Conda, check out the following links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.freecodecamp.org/why-you-need-python-environments-and-how-to-manage-them-with-conda-85f155f4353c&quot;&gt;Why you need Python environments and how to manage them with Conda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://jakevdp.github.io/blog/2016/08/25/conda-myths-and-misconceptions/&quot;&gt;Conda: Myths and Misconceptions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Python Community Interview With Michael Kennedy</title>
      <id>https://realpython.com/interview-michael-kennedy/</id>
      <link href="https://realpython.com/interview-michael-kennedy/"/>
      <updated>2018-10-29T14:00:00+00:00</updated>
      <summary>Michael Kennedy is the host of the most popular Python podcast, Talk Python to Me, as well as a co-host on the Python Bytes podcast. Find out why he got started with Python, how he manages to produce so much Python content, and what he thinks about while he&#39;s in traffic!</summary>
      <content type="html">
        &lt;p&gt;This week, our Python community interview is with none other than &lt;a href=&quot;https://twitter.com/mkennedy&quot;&gt;Michael Kennedy&lt;/a&gt; of &lt;em&gt;Talk Python to Me&lt;/em&gt; fame. &lt;/p&gt;
&lt;p&gt;You may know his authoritative voice, but do you know his Python story? Read on to find out about his journey with Python, what he thinks about when stuck in traffic, and his love for two wheels.&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! If I recall correctly, you started out as a .NET developer, and you were even a Microsoft Certified Trainer. So I’m curious as to how you came to Python and what made you stick around?&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/Michael_Kennedy.d8ffce2ca99e.jpg&quot; width=&quot;700&quot; height=&quot;700&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Michael_Kennedy.d8ffce2ca99e.jpg&amp;amp;w=175&amp;amp;sig=a06413ab02ec6f61234309cc603bf9ce7ef35b4c 175w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Michael_Kennedy.d8ffce2ca99e.jpg&amp;amp;w=350&amp;amp;sig=af5d31c2499a9282c371c197e7ae92aafedb3a9f 350w, https://files.realpython.com/media/Michael_Kennedy.d8ffce2ca99e.jpg 700w&quot; sizes=&quot;75vw&quot; alt=&quot;Michael Kennedy&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Michael:&lt;/strong&gt; Thanks for having me. Oh, this brings back memories. Yes, I was doing full time .NET development with C# for probably 10 years. It&amp;rsquo;s a language I still respect today. &lt;/p&gt;
&lt;p&gt;I found my way to Python after wanting to branch out into areas outside of the Microsoft space. I guess this was probably 2012 or around then. I hadn&amp;rsquo;t been doing much outside of C++ and C# before then for some time other than JavaScript. (No one escapes JavaScript!) I looked at the popular languages, and this was just around the time Python was becoming popular and increasingly so.&lt;/p&gt;
&lt;p&gt;I spent a few weeks learning Python and was pretty much hooked but didn&amp;rsquo;t know it. &lt;/p&gt;
&lt;p&gt;I studied the language and ecosystem and found it to be really nice&amp;mdash;much nicer than I expected. But I suffered the problem that everyone who knows some language really well does when they try something different. Everything I knew by heart was a challenge again. How do I create a web app? How do I host it? How do I query a database? And on and on.&lt;/p&gt;
&lt;p&gt;I was willing to learn and did. But it was just that uneasiness that anyone would have, giving up the familiar path. However, I knew I was going to be hooked when I went back to write some C# code and found it way less tasteful than I had just a few weeks prior. &lt;/p&gt;
&lt;p&gt;This is not to bash that language. But like all C-based languages, it had a lot of symbol noise, let&amp;rsquo;s say. Why do we need semicolons again? Why the massive love for parentheses and curly braces even when it&amp;rsquo;s (now) clear they are unneeded, etc. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;div class=&quot;highlight csharp&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;Thing&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TotalSales&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&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;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;total&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;value&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;total&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;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;Thing&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;total_sales&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;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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;total&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;n&quot;&gt;value&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Makes one wonder why you&amp;rsquo;ve been typing all those symbols, all those years. &lt;/p&gt;
&lt;p&gt;Since then, as I&amp;rsquo;ve learned more and more of the popular packages and standard library modules, I&amp;rsquo;ve just enjoyed it more every day. Now I run my entire business on a Python stack, and it has yet to let me down. &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, the host of the most popular Python podcast&amp;mdash;Talk Python to Me&amp;mdash;which now has over 180 episodes. You&amp;rsquo;re also a co-host on the &lt;/em&gt;Python Bytes&lt;em&gt; podcast with Brian Okken. That’s a lot of content! How do you continue to be so consistent each week, and keep the shows relevant and newsworthy? It must be a lot of work?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Michael:&lt;/strong&gt; That is definitely a lot of content. But it&amp;rsquo;s a very rewarding project that is into its fourth year for &lt;a href=&quot;https://talkpython.fm/&quot;&gt;&lt;em&gt;Talk Python To Me&lt;/em&gt;&lt;/a&gt;, and third year for &lt;a href=&quot;https://pythonbytes.fm/&quot;&gt;&lt;em&gt;Python Bytes&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;How am I still consistent? That&amp;rsquo;s a good question. I started the podcast because others were inconsistent. There have been Python-based podcasts before mine. But they all stopped producing episodes long before I got in the game. In fact, that&amp;rsquo;s why I felt I could get started, because there was such a lack of content.&lt;/p&gt;
&lt;p&gt;I am consistent for a few reasons. First, when I started the podcast, I promised myself I&amp;rsquo;d do it every week for six months and then decide whether the community and I enjoyed it. After that much consistent content creation, you are pretty deep within the habit of doing so. &lt;/p&gt;
&lt;p&gt;Second, by then I had several companies sponsoring the podcast. I thought maybe, just maybe, I could find a way to use the podcast to become independent of my day job. I didn&amp;rsquo;t hate my day job, but it doesn&amp;rsquo;t compare to doing what you think is truly valuable for the community and world. Once you accept money to produce a thing over a long period of time, consistency is just part of the agreement. &lt;/p&gt;
&lt;p&gt;Finally, the listeners were so supportive of my work. It genuinely felt great producing the content for everyone. I looked forward to each episode I created. After all, I was learning so much from each one and continue to do so to this day.&lt;/p&gt;
&lt;p&gt;As for keeping the show relevant and newsworthy, that is easy. For &lt;em&gt;Python Bytes&lt;/em&gt;, that&amp;rsquo;s literally the topic (weekly news items), and we get tons of help from listeners all suggesting great new items each week. &lt;/p&gt;
&lt;p&gt;For &lt;em&gt;Talk Python To Me&lt;/em&gt;, this is harder. Each episode digs deeply into a topic. For the first 20 episodes or so, that was easy enough for me. I&amp;rsquo;d used SQLAlchemy for example, so asking Mike Bayer about it was just thinking back on my experience. But it quickly grew into spaces I had little experience with. I now spend quite a bit of time researching topics to cover each week. Any given episode has between 4&amp;ndash;8 hours of research before we even press record.&lt;/p&gt;
&lt;p&gt;That leads into the final part of your question: Yes, it is a lot of work. I&amp;rsquo;ve had folks ask me how much time I spend on the show each week. They&amp;rsquo;ve even said, &amp;ldquo;You have such a sweet deal. What do you spend on the podcast per episode? A few hours?&amp;rdquo; Well, that would be something! I probably spend about 2 days per episode between the research, outreach to guests, email correspondence, website development, sponsorship relationships, and much more.&lt;/p&gt;
&lt;p&gt;That is a lot of time, but it is also literally the foundation of my business. The podcasts and the courses only work if they are both well known and high quality. It&amp;rsquo;s very fortunate that I&amp;rsquo;ve been able to transition my part-time podcast into a full-time job (podcast and courses). It lets me really stay focused and stay consistent.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;If our readers don’t know you from the podcast, they surely will know you for your excellent courses on Talk Python Training. One of the first courses I took when I began learning Python was your Python Jumpstart by Building 10 Apps course&amp;mdash;which is excellent by the way. And you’ve just released a new course named Async Techniques and Examples. Could you tell us a little more about it and why you decided to focus on Async, specifically?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Michael:&lt;/strong&gt; Thanks! The courses have been a true passion project for me. I&amp;rsquo;ve wanted to create the best online library for Python courses out there for a long time. &lt;/p&gt;
&lt;p&gt;When I first started the podcast, I also wanted to start the courses. I saw them as going hand in hand, with each supporting the other. However, at the time I worked for a company that did in-person and online training courses for developers.&lt;/p&gt;
&lt;p&gt;They afforded me a lot of freedom and flexibility. But what would not fly is my creating effectively a competing company in my spare time. So I started with the podcast, and then once I could go full time independently, my first action was to launch the training company and &lt;a href=&quot;https://training.talkpython.fm/courses/explore_python_jumpstart/python-language-jumpstart-building-10-apps&quot;&gt;Python Jumpstart by Building 10 Apps&lt;/a&gt; on &lt;a href=&quot;https://www.kickstarter.com/projects/mikeckennedy/python-jumpstart-by-building-10-apps-video-course&quot;&gt;Kickstarter&lt;/a&gt;. That was a really fun experience and a huge success.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://training.talkpython.fm/courses/explore_async_python/async-in-python-with-threading-and-multiprocessing&quot;&gt;new async course&lt;/a&gt; is super fun and something I felt really needed to exist for the community. It needs to exist for a few reasons. The Python async/concurrent programming story is a little hard to understand and make sense of, for many people. We have the GIL, which actually is covered very nicely on &lt;em&gt;Real Python&lt;/em&gt;. This means normal threading was only effective for IO bound work. CPU bound work requires another API, multiprocessing with its own oddities and techniques. &lt;/p&gt;
&lt;p&gt;Now, as of Python 3.5, we have the amazing &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; keywords. They are powerful and clean but add more choices and more fog to the situation. This doesn&amp;rsquo;t take into account the async features of Cython and its &lt;code&gt;nogil&lt;/code&gt; keyword.&lt;/p&gt;
&lt;p&gt;So the first reason is there was a lot of confusion around async and Python. You hear of people leaving Python for Go explicitly because of Go&amp;rsquo;s &amp;ldquo;better&amp;rdquo; concurrency. Usually, the type of concurrency people are looking for is IO bound, which works extremely well in Python anyway.&lt;/p&gt;
&lt;p&gt;The next reason is that async and concurrent programming is oddly taught in the wrong order. What I mean by this is that usually lots of confusing, low-level detail is presented up front. Then finally it&amp;rsquo;s put together into examples that are useful and compelling. But the learner has to make it that far for it to pay off. This is also often paired with dire warnings of thread safety and how hard race conditions are. All of this is true and accurate. But why start there? &lt;/p&gt;
&lt;p&gt;I wanted a course that shows how productive, fun, and actually easy async is for many cases. Once the student sees the value, then you can dive into things like thread safety and so on.&lt;/p&gt;
&lt;p&gt;Finally, there really just are not many async courses for Python out there. I only know of one other one, and it&amp;rsquo;s behind a subscription wall.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;It’s no secret that you’re a big fan of MongoDB. What do you find most appealing about it? And if someone has never used it before, why might they consider using it with their next Python project?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Michael:&lt;/strong&gt; I am a big fan of MongoDB. Long ago, I was complaining to a friend about how painful deploying relational database apps was. About how it&amp;rsquo;s a pain to apply the migration scripts without downtime and things like that. He said, &amp;ldquo;Well, why don&amp;rsquo;t you just use MongoDB, and you won&amp;rsquo;t have that problem?&amp;rdquo;. &lt;/p&gt;
&lt;p&gt;I took his advice, and he was right! Since then, I&amp;rsquo;ve launched 4 or 5 major projects on MongoDB. Both of my podcast sites (&lt;a href=&quot;https://talkpython.fm/&quot;&gt;talkpython.fm&lt;/a&gt; and &lt;a href=&quot;https://pythonbytes.fm/&quot;&gt;pythonbytes.fm&lt;/a&gt;) and the course site run on MongoDB. &lt;/p&gt;
&lt;p&gt;I know some folks have had bad experiences with MongoDB. There were a few &amp;ldquo;best practices&amp;rdquo; that were not the default in MongoDB in the early days, and there are lots of stories about these. Most of them have been fixed, and if you know to avoid them, you&amp;rsquo;re in good shape. The &lt;a href=&quot;https://docs.mongodb.com/manual/administration/security-checklist/&quot;&gt;one major gotcha still out there&lt;/a&gt; is that MongoDB runs without authentication unless you explicitly create an account. &lt;/p&gt;
&lt;p&gt;That said, MongoDB has been totally bulletproof for me and my projects. It&amp;rsquo;s been powering these sites mentioned above for years without any downtime. I have literally never run a migration or upgrade script to deploy a new schema or data model.&lt;/p&gt;
&lt;p&gt;Mongo&amp;rsquo;s flexible schema model and MongoEngine&amp;rsquo;s class-based ODM (think ORM for MongoDB) are just right for my projects. The websites run with super low latency. It&amp;rsquo;s pretty common to get 10&amp;ndash;20 ms response time for non-trivial pages (from request to response out of the server).&lt;/p&gt;
&lt;p&gt;I personally can totally recommend MongoDB for your projects. I also created a free course if people are interested at &lt;a href=&quot;http://freemongodbcourse.com/&quot;&gt;freemongodbcourse.com&lt;/a&gt;.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Now the Python is out of the way, it’s time to talk about the fun stuff… Math! You have a master’s degree in Mathematics, and you did start your Ph.D. Any plans to finish it in the future, or has that ship sailed? I would imagine you still have a passion for it. Do you get to scratch that itch on a daily basis?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Michael:&lt;/strong&gt; It&amp;rsquo;s sailed, over the horizon, and halfway to Antarctica! I did study math and still very much appreciate the beauty of everything about it. I was just thinking about the different types of infinity, the different sizes of infinity, on just the simple number line between [0, 1] while stuck in traffic last week. &lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s not working in math, day to day. I believe software development is my true calling. I love doing it every day. What I learned in math was excellent preparation for development. The rules of mathematics and the &amp;ldquo;rules&amp;rdquo; (language, APIs, algorithms, big-O, etc.) of software are surprisingly similar. The types of thinking and problem solving are also quite comparable.&lt;/p&gt;
&lt;p&gt;The career opportunity in software and entrepreneurship vs. mathematics is not comparable. It&amp;rsquo;s just better to build things people can use (software) rather than theories only 5&amp;ndash;20 people in the world will understand and care about (math these days).&lt;/p&gt;
&lt;p&gt;So I love it and still &lt;a href=&quot;https://realpython.com/asins/0691168482/&quot;&gt;read books about it&lt;/a&gt;, but I&amp;rsquo;m not doing anything practical with math these days.&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 other hobbies or interests, aside from Python?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Michael:&lt;/strong&gt; I&amp;rsquo;ve had many fun hobbies over the years. I do think it&amp;rsquo;s important to have a balance between computer time and other things in your life. &lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m pretty fortunate in that I basically have 3 really engaging aspects to my job that I would almost consider hobbies. I run the podcast and am really into improving that craft and connecting with the listeners. I am doing software development still almost daily. And running my business and the whole entrepreneurial side of things is amazing and fun.&lt;/p&gt;
&lt;p&gt;In terms of actual hobbies, I love racing and anything with two wheels! I grew up racing BMX bikes in grades 1&amp;ndash;5, then motocross through middle school and high school, and finally mountain bikes in college. My brothers and I built a motocross track in our backyard, and it was pretty common to come home from school, drop our backpacks, and spend an hour or two challenging each other to clear this series of jumps or just having a fun time.&lt;/p&gt;
&lt;p&gt;These days, I only watch motocross and am also a huge fan of IndyCar. I still ride but keep it to mellow adventures with my wife on our street motorcycles around the mountains here in Portland, OR. It&amp;rsquo;s great to be able to share that experience of riding with her and my daughters, who jump on the back of one of our bikes.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;What do we have to look forward to in the future from Talk Python? Any secret projects you’d like to tell us about or anything you’d like to share and/or plug?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Michael:&lt;/strong&gt; I have some projects involving exciting courses coming out. I&amp;rsquo;d actually like to be more forthcoming with what I&amp;rsquo;m working on there, but there is a surprising amount of &amp;ldquo;copying&amp;rdquo; my popular courses let&amp;rsquo;s say.&lt;/p&gt;
&lt;p&gt;We have at least 4 courses in active development right now and a massive list of things we&amp;rsquo;d like to build. So in terms of courses, just expect us to keep working on new ones that we see the need for in the community. You can also expect some more world-class authors there creating content. I&amp;rsquo;m really honored to be able to work on the courses for everyone and make my dream of this resource and business a reality.&lt;/p&gt;
&lt;p&gt;In terms of the podcast, no slowing down there. We have &lt;em&gt;Talk Python&lt;/em&gt; and &lt;em&gt;Python Bytes&lt;/em&gt;, and both are going strong. I just hope to bring better and deeper stories to the community on &lt;em&gt;Talk Python&lt;/em&gt; and stay on top of the most exciting programming language out there with weekly updates on &lt;em&gt;Python Bytes&lt;/em&gt; with my co-host Brian Okken.&lt;/p&gt;
&lt;p&gt;Thank you all for having me here on &lt;em&gt;Real Python&lt;/em&gt;. I&amp;rsquo;m a big fan of the resource you all have created. If readers are interested in my projects, please subscribe to the podcasts at &lt;a href=&quot;https://talkpython.fm/&quot;&gt;talkpython.fm&lt;/a&gt; and &lt;a href=&quot;https://pythonbytes.fm/&quot;&gt;pythonbytes.fm&lt;/a&gt;. If they have aspects of Python they&amp;rsquo;d like to learn either personally or for their team, check out our 100+ hours of courses at &lt;a href=&quot;https://training.talkpython.fm/&quot;&gt;training.talkpython.fm&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Thank you, &lt;a href=&quot;https://twitter.com/mkennedy&quot;&gt;Michael&lt;/a&gt;, for joining me for this week’s interview. It&amp;rsquo;s been great having you on the other side of the interview mic. &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>Practical Text Classification With Python and Keras</title>
      <id>https://realpython.com/python-keras-text-classification/</id>
      <link href="https://realpython.com/python-keras-text-classification/"/>
      <updated>2018-10-24T14:00:00+00:00</updated>
      <summary>Learn about Python text classification with Keras. Work your way from a bag-of-words model with logistic regression to more advanced methods leading to convolutional neural networks. See why word embeddings are useful and how you can use pretrained word embeddings. Use hyperparameter optimization to squeeze more performance out of your model.</summary>
      <content type="html">
        &lt;p&gt;Imagine you could know the mood of the people on the Internet. Maybe you are not interested in its entirety, but only if people are today happy on your favorite social media platform. After this tutorial, you&amp;rsquo;ll be equipped to do this. While doing this, you will get a grasp of current advancements of (deep) neural networks and how they can be applied to text.&lt;/p&gt;
&lt;p&gt;Reading the mood from text with machine learning is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Sentiment_analysis&quot;&gt;sentiment analysis&lt;/a&gt;, and it is one of the prominent use cases in text classification. This falls into the very active research field of &lt;a href=&quot;https://en.wikipedia.org/wiki/Natural_language_processing&quot;&gt;natural language processing (NLP)&lt;/a&gt;. Other common use cases of text classification include detection of spam, auto tagging of customer queries, and categorization of text into defined topics. So how can you do this?&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;choosing-a-data-set&quot;&gt;Choosing a Data Set&lt;/h2&gt;
&lt;p&gt;Before we start, let&amp;rsquo;s take a look at what data we have. Go ahead and download the data set from the &lt;a href=&quot;https://archive.ics.uci.edu/ml/datasets/Sentiment+Labelled+Sentences&quot;&gt;Sentiment Labelled Sentences Data Set&lt;/a&gt; from the UCI Machine Learning Repository.&lt;/p&gt;
&lt;p&gt;By the way, this repository is a wonderful source for machine learning data sets when you want to try out some algorithms. This data set includes labeled reviews from IMDb, Amazon, and Yelp. Each review is marked with a score of 0 for a negative sentiment or 1 for a positive sentiment.&lt;/p&gt;
&lt;p&gt;Extract the folder into a &lt;code&gt;data&lt;/code&gt; folder and go ahead and load the data with &lt;a href=&quot;https://pandas.pydata.org/&quot;&gt;Pandas&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;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;n&quot;&gt;filepath_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;s1&quot;&gt;&amp;#39;yelp&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/sentiment_analysis/yelp_labelled.txt&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                 &lt;span class=&quot;s1&quot;&gt;&amp;#39;amazon&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/sentiment_analysis/amazon_cells_labelled.txt&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                 &lt;span class=&quot;s1&quot;&gt;&amp;#39;imdb&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/sentiment_analysis/imdb_labelled.txt&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;df_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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filepath_dict&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;n&quot;&gt;df&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;n&quot;&gt;filepath&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;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;sentence&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;label&amp;#39;&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;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&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;df&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Add another column filled with the source name&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;df_list&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;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;df&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;concat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df_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;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iloc&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;sentence    Wow... Loved this place.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;label                              1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;source                          yelp&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Name: 0, dtype: object&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This looks about right. With this data set, you are able to train a model to predict the sentiment of a sentence. Take a quick moment to think about how you would go about predicting the data.&lt;/p&gt;
&lt;p&gt;One way you could do this is to count the frequency of each word in each sentence and tie this count back to the entire set of words in the data set. You would start by taking the data and creating a vocabulary from all the words in all sentences. The collection of texts is also called a &lt;strong&gt;corpus&lt;/strong&gt; in NLP. &lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;vocabulary&lt;/strong&gt; in this case is a list of words that occurred in our text where each word has its own index. This enables you to create a vector for a sentence. You would then take the sentence you want to vectorize, and you count each occurrence in the vocabulary. The resulting vector will be with the length of the vocabulary and a count for each word in the vocabulary. &lt;/p&gt;
&lt;p&gt;The resulting vector is also called a &lt;strong&gt;feature vector&lt;/strong&gt;. In a feature vector, each dimension can be a numeric or categorical feature, like for example the height of a building, the price of a stock, or, in our case, the count of a word in a vocabulary. These feature vectors are a crucial piece in data science and machine learning, as the model you want to train depends on them.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s quickly illustrate this. Imagine you have the following two sentences:&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;sentences&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;John likes ice cream&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;John hates chocolate.&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 can use the &lt;a href=&quot;http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html&quot;&gt;&lt;code&gt;CountVectorizer&lt;/code&gt;&lt;/a&gt; provided by the &lt;a href=&quot;http://scikit-learn.org/&quot;&gt;scikit-learn&lt;/a&gt; library to vectorize sentences. It takes the words of each sentence and creates a vocabulary of all the unique words in the sentences. This vocabulary can then be used to create a feature vector of the count of the words:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sklearn.feature_extraction.text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CountVectorizer&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;vectorizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CountVectorizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;min_df&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;lowercase&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vectorizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences&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;vectorizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vocabulary_&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;John&amp;#39;: 0, &amp;#39;chocolate&amp;#39;: 1, &amp;#39;cream&amp;#39;: 2, &amp;#39;hates&amp;#39;: 3, &amp;#39;ice&amp;#39;: 4, &amp;#39;likes&amp;#39;: 5}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This vocabulary serves also as an index of each word. Now, you can take each sentence and get the word occurrences of the words based on the previous vocabulary. The vocabulary consists of all five words in our sentences, each representing one word in the vocabulary. When you take the previous two sentences and transform them with the &lt;code&gt;CountVectorizer&lt;/code&gt; you will get a vector representing the count of each word of the sentence:&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;vectorizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences&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;toarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;array([[1, 0, 1, 0, 1, 1],&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    [1, 1, 0, 1, 0, 0]])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, you can see the resulting feature vectors for each sentence based on the previous vocabulary. For example, if you take a look at the first item, you can see that both vectors have a &lt;code&gt;1&lt;/code&gt; there. This means that both sentences have one occurrence of &lt;code&gt;John&lt;/code&gt;, which is in the first place in the vocabulary.&lt;/p&gt;
&lt;p&gt;This is considered a &lt;a href=&quot;https://en.wikipedia.org/wiki/Bag-of-words_model&quot;&gt;Bag-of-words (BOW)&lt;/a&gt; model, which is a common way in NLP to create vectors out of text. Each document is represented as a vector. You can use these vectors now as feature vectors for a machine learning model. This leads us to our next part, defining a baseline model.&lt;/p&gt;
&lt;h2 id=&quot;defining-a-baseline-model&quot;&gt;Defining a Baseline Model&lt;/h2&gt;
&lt;p&gt;When you work with machine learning, one important step is to define a baseline model. This usually involves a simple model, which is then used as a comparison with the more advanced models that you want to test. In this case, you&amp;rsquo;ll use the baseline model to compare it to the more advanced methods involving (deep) neural networks, the meat and potatoes of this tutorial.&lt;/p&gt;
&lt;p&gt;First, you are going to &lt;a href=&quot;http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html&quot;&gt;split the data into a training and testing set&lt;/a&gt; which will allow you to evaluate the accuracy and see if your model generalizes well. This means whether the model is able to perform well on data it has not seen before. This is a way to see if the model is overfitting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overfitting&lt;/strong&gt; is when a model is trained too well on the training data. You want to avoid overfitting, as this would mean that the model mostly just memorized the training data. This would account for a large accuracy with the training data but a low accuracy in the testing data. &lt;/p&gt;
&lt;p&gt;We start by taking the Yelp data set which we extract from our concatenated data set. From there, we take the sentences and labels. The &lt;code&gt;.values&lt;/code&gt; returns a &lt;a href=&quot;https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.array.html&quot;&gt;NumPy array&lt;/a&gt; instead of a &lt;a href=&quot;http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html&quot;&gt;Pandas Series&lt;/a&gt; object which is in this context easier to work with:&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;sklearn.model_selection&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;train_test_split&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;df_yelp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&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;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;yelp&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;sentences&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df_yelp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sentence&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;values&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df_yelp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;label&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;values&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;sentences_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentences_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;train_test_split&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;sentences&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;test_size&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;random_state&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we will use again on the previous BOW model to vectorize the sentences. You can use again the &lt;code&gt;CountVectorizer&lt;/code&gt; for this task. Since you might not have the testing data available during training, you can create the vocabulary using only the training data. Using this vocabulary, you can create the feature vectors for each sentence of the training and testing set:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sklearn.feature_extraction.text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CountVectorizer&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;vectorizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CountVectorizer&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;vectorizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_train&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;X_train&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vectorizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_train&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;X_test&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vectorizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_test&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;X_train&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;750x1714 sparse matrix of type &amp;#39;&amp;lt;class &amp;#39;numpy.int64&amp;#39;&amp;gt;&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    with 7368 stored elements in Compressed Sparse Row format&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see that the resulting feature vectors have 750 samples which are the number of training samples we have after the train-test split. Each sample has 1714 dimensions which is the size of the vocabulary. Also, you can see that we get a &lt;a href=&quot;https://docs.scipy.org/doc/scipy/reference/sparse.html&quot;&gt;sparse matrix&lt;/a&gt;. This is a data type that is optimized for matrices with only a few non-zero elements, which only keeps track of the non-zero elements reducing the memory load. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;CountVectorizer&lt;/code&gt; performs &lt;a href=&quot;https://en.wikipedia.org/wiki/Lexical_analysis#Tokenization&quot;&gt;tokenization&lt;/a&gt; which separates the sentences into a set of &lt;strong&gt;tokens&lt;/strong&gt; as you saw previously in the vocabulary. It additionally removes punctuation and special characters and can apply other preprocessing to each word.  If you want, you can use a custom tokenizer from the &lt;a href=&quot;https://www.nltk.org/&quot;&gt;NLTK&lt;/a&gt; library with the &lt;code&gt;CountVectorizer&lt;/code&gt; or use any number of the customizations which you can explore to improve the performance of your model.&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; There are a lot of additional parameters to &lt;code&gt;CountVectorizer()&lt;/code&gt; that we forgo using here, such as adding &lt;strong&gt;ngrams&lt;/strong&gt;, beacuse the goal at first is to build a simple baseline model. The token pattern itself defaults to &lt;code&gt;token_pattern=’(?u)\b\w\w+\b’&lt;/code&gt;, which is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Regular_expression&quot;&gt;regex&lt;/a&gt; pattern that says, &amp;ldquo;a word is 2 or more Unicode word characters surrounded by word boundaries.&amp;rdquo;. &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The classification model we are going to use is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Logistic_regression&quot;&gt;logistic regression&lt;/a&gt; which is a simple yet powerful linear model that is mathematically speaking in fact a form of regression between 0 and 1 based on the input feature vector. By specifying a cutoff value (by default 0.5), the regression model is used for classification. You can use again scikit-learn library which provides the &lt;code&gt;LogisticRegression&lt;/code&gt; classifier:&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;sklearn.linear_model&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LogisticRegression&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;classifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LogisticRegression&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;classifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&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;score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&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;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&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;s2&quot;&gt;&amp;quot;Accuracy:&amp;quot;&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;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Accuracy: 0.796&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see that the logistic regression reached an impressive 79.6%, but let&amp;rsquo;s have a look how this model performs on the other data sets that we have. In this script, we perform and evaluate the whole process for each data set that we have:&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;source&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;df_source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&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;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sentences&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sentence&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;values&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;df_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;label&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;values&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;sentences_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentences_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;train_test_split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sentences&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;test_size&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;random_state&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;vectorizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CountVectorizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vectorizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vectorizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vectorizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LogisticRegression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&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;classifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&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;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&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;Accuracy for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; data: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&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;source&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;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s the result:&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;Accuracy for yelp data: 0.7960&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Accuracy for amazon data: 0.7960&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Accuracy for imdb data: 0.7487&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Great! You can see that this fairly simple model achieves a fairly good accuracy. It would be interesting to see whether we are able to outperform this model. In the next part, we will get familiar with (deep) neural networks and how to apply them to text classification.&lt;/p&gt;
&lt;h2 id=&quot;a-primer-on-deep-neural-networks&quot;&gt;A Primer on (Deep) Neural Networks&lt;/h2&gt;
&lt;p&gt;You might have experienced some of the excitement and fear related to artificial intelligence and deep learning. You might have stumbled across some confusing article or concerned TED talk about the approaching &lt;a href=&quot;https://en.wikipedia.org/wiki/Technological_singularity&quot;&gt;singularity&lt;/a&gt; or maybe you saw the &lt;a href=&quot;https://www.youtube.com/watch?v=knoOXBLFQ-s&quot;&gt;backflipping robots&lt;/a&gt; and you wonder whether a life in the woods seems reasonable after all.&lt;/p&gt;
&lt;p&gt;On a lighter note, AI researchers all agreed that they did not agree with each other when AI will exceed Human-level performance. According to this &lt;a href=&quot;https://arxiv.org/abs/1705.08807&quot;&gt;paper&lt;/a&gt; we should still have some time left.&lt;/p&gt;
&lt;p&gt;So you might already be curious how neural networks work. If you already are familiar with neural networks, feel free to skip to the parts involving Keras. Also, there is the wonderful &lt;a href=&quot;http://www.deeplearningbook.org/&quot;&gt;Deep Learning book&lt;/a&gt; by Ian Goodfellow which I highly recommend if you want to dig deeper into the math. You can read the whole book online for free. In this section you will get an overview of neural networks and their inner workings, and you will later see how to use neural networks with the outstanding Keras library.&lt;/p&gt;
&lt;p&gt;In this article, you don&amp;rsquo;t have to worry about the singularity, but (deep) neural networks play a crucial role in the latest developments in AI. It all started with a famous &lt;a href=&quot;https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf&quot;&gt;paper&lt;/a&gt; in 2012 by &lt;a href=&quot;https://en.wikipedia.org/wiki/Geoffrey_Hinton&quot;&gt;Geoffrey Hinton&lt;/a&gt; and his team, which outperformed all previous models in the famous &lt;a href=&quot;https://en.wikipedia.org/wiki/ImageNet#ImageNet_Challenge&quot;&gt;ImageNet Challenge&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The challenge could be considered the World Cup in computer vision which involves classifying a large set of images based on given labels. Geoffrey Hinton and his team managed to beat the previous models by using a &lt;a href=&quot;https://en.wikipedia.org/wiki/Convolutional_neural_network&quot;&gt;convolutional neural network (CNN)&lt;/a&gt;, which we will cover in this tutorial as well.&lt;/p&gt;
&lt;p&gt;Since then, neural networks have moved into several fields involving classification, regression and even generative models. The most prevalent fields include computer vision, voice recognition and natural language processing (NLP).&lt;/p&gt;
&lt;p&gt;Neural networks, or sometimes called artificial neural network (ANN) or feedforward neural network, are computational networks which were vaguely inspired by the neural networks in the human brain. They consist of neurons (also called nodes) which are connected like in the graph below.&lt;/p&gt;
&lt;p&gt;You start by having a layer of input neurons where you feed in your feature vectors and the values are then feeded forward to a hidden layer. At each connection, you are feeding the value forward, while the value is multiplied by a weight and a bias is added to the value. This happens at every connection and at the end you reach an output layer with one or more output nodes.&lt;/p&gt;
&lt;p&gt;If you want to have a binary classification you can use one node, but if you have multiple categories you should use multiple nodes for each category:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/ANN.a2284c5d07a3.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/ANN.a2284c5d07a3.png&quot; width=&quot;1280&quot; height=&quot;720&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ANN.a2284c5d07a3.png&amp;amp;w=320&amp;amp;sig=72b65863dab887ef34f4bebf488084eaa30f86ef 320w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ANN.a2284c5d07a3.png&amp;amp;w=640&amp;amp;sig=0b3dab94efca95d4b9dad5f8b1da5d1175f11b7e 640w, https://files.realpython.com/media/ANN.a2284c5d07a3.png 1280w&quot; sizes=&quot;75vw&quot; alt=&quot;neural network structure&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Neural network model&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;You can have as many hidden layers as you wish. In fact, a neural network with more than one hidden layer is considered a deep neural network. Don&amp;rsquo;t worry: I won&amp;rsquo;t get here into the mathematical depths concerning neural networks. But if you want to get an intuitive visual understanding of the math involved, you can check out the &lt;a href=&quot;https://www.youtube.com/playlist?list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi&quot;&gt;YouTube Playlist&lt;/a&gt; by Grant Sanderson. The formula from one layer to the next is this short equation:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/njanakiev-neural-network-formula.059738905ae8.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/njanakiev-neural-network-formula.059738905ae8.png&quot; width=&quot;289&quot; height=&quot;84&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/njanakiev-neural-network-formula.059738905ae8.png&amp;amp;w=72&amp;amp;sig=c2f6e07d3765f93e9441d82559df70205758ddb7 72w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/njanakiev-neural-network-formula.059738905ae8.png&amp;amp;w=144&amp;amp;sig=bea547b1e4e718d0f733fd78146d4c48dc24028f 144w, https://files.realpython.com/media/njanakiev-neural-network-formula.059738905ae8.png 289w&quot; sizes=&quot;75vw&quot; alt=&quot;neural network formula&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Neural network formula&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Let&amp;rsquo;s slowly unpack what is happening here. You see, we are dealing here with only two layers. The layer with nodes &lt;code&gt;a&lt;/code&gt; serves as input for the layer with nodes &lt;code&gt;o&lt;/code&gt;. In order to calculate the values for each output node, we have to multiply each input node by a weight &lt;code&gt;w&lt;/code&gt; and add a bias &lt;code&gt;b&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All of those have to be then summed and passed to a function &lt;code&gt;f&lt;/code&gt;. This function is considered the &lt;a href=&quot;https://en.wikipedia.org/wiki/Activation_function&quot;&gt;activation function&lt;/a&gt; and there are various different functions that can be used depending on the layer or the problem. It is generally common to use a &lt;a href=&quot;https://en.wikipedia.org/wiki/Rectifier_(neural_networks)&quot;&gt;rectified linear unit (ReLU)&lt;/a&gt; for hidden layers, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Sigmoid_function&quot;&gt;sigmoid function&lt;/a&gt; for the output layer in a binary classification problem, or a &lt;a href=&quot;https://en.wikipedia.org/wiki/Softmax_function&quot;&gt;softmax function&lt;/a&gt; for the output layer of multi-class classification problems.&lt;/p&gt;
&lt;p&gt;You might already wonder how the weights are calculated, and this is obviously the most important part of neural networks, but also the most difficult part. The algorithm starts by initializing the weights with random values and they are then trained with a method called &lt;a href=&quot;https://en.wikipedia.org/wiki/Backpropagation&quot;&gt;backpropagation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is done by using optimization methods (also called optimizer) like the &lt;a href=&quot;https://en.wikipedia.org/wiki/Gradient_descent&quot;&gt;gradient descent&lt;/a&gt; in order to reduce the error between the computed and the desired output (also called target output). The error is determined by a &lt;a href=&quot;https://en.wikipedia.org/wiki/Loss_function&quot;&gt;loss function&lt;/a&gt; whose loss we want to minimize with the optimizer. The whole process is too extensive to cover here, but I&amp;rsquo;ll refer again to the Grant Sanderson playlist and the Deep Learning book by Ian Goodfellow I mentioned before.&lt;/p&gt;
&lt;p&gt;What you have to know is that there are various optimization methods that you can use, but the most common optimizer currently used is called &lt;a href=&quot;https://arxiv.org/abs/1412.6980&quot;&gt;Adam&lt;/a&gt; which has a good performance in various problems.&lt;/p&gt;
&lt;p&gt;You can also use different loss functions, but in this tutorial you will only need the &lt;a href=&quot;https://en.wikipedia.org/wiki/Cross_entropy&quot;&gt;cross entropy&lt;/a&gt; loss function or more specifically binary cross entropy which is used for binary classification problems. Be sure to experiment with the various available methods and tools. Some researchers even claim in a recent &lt;a href=&quot;http://www.sciencemag.org/news/2018/05/ai-researchers-allege-machine-learning-alchemy?es_p=6976017&quot;&gt;article&lt;/a&gt; that the choice for the best performing methods borders on alchemy. The reason being that many methods are not well explained and consist of a lot of tweaking and testing.&lt;/p&gt;
&lt;h3 id=&quot;introducing-keras&quot;&gt;Introducing Keras&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://keras.io/&quot;&gt;Keras&lt;/a&gt; is a deep learning and neural networks API by &lt;a href=&quot;https://twitter.com/fchollet&quot;&gt;François Chollet&lt;/a&gt; which is capable of running on top of &lt;a href=&quot;https://www.tensorflow.org/&quot;&gt;Tensorflow&lt;/a&gt; (Google), &lt;a href=&quot;http://deeplearning.net/software/theano/&quot;&gt;Theano&lt;/a&gt; or &lt;a href=&quot;https://www.microsoft.com/en-us/cognitive-toolkit/&quot;&gt;CNTK&lt;/a&gt; (Microsoft). To quote the wonderful book by François Chollet, &lt;em&gt;Deep Learning with Python&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Keras is a model-level library, providing high-level building blocks for developing deep-learning models. It doesn’t handle low-level operations such as tensor manipulation and differentiation. Instead, it relies on a specialized, well-optimized tensor library to do so, serving as the backend engine of Keras (&lt;a href=&quot;https://realpython.com/asins/1617294438/&quot;&gt;Source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is a great way to start experimenting with neural networks without having to implement every layer and piece on your own. For example Tensorflow is a great machine learning library, but you have to implement a lot of boilerplate code to have a model running.&lt;/p&gt;
&lt;h3 id=&quot;installing-keras&quot;&gt;Installing Keras&lt;/h3&gt;
&lt;p&gt;Before installing Keras, you&amp;rsquo;ll need either Tensorflow, Theano, or CNTK. In this tutorial we will be using Tensorflow so check out their installation guide &lt;a href=&quot;https://github.com/tensorflow/tensorflow#download-and-setup&quot;&gt;here&lt;/a&gt;, but feel free to use any of the frameworks that works best for you. Keras can be installed using &lt;a href=&quot;https://pypi.org/&quot;&gt;PyPI&lt;/a&gt; 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; pip install keras
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can choose the backend you want to have by opening the Keras configuration file which you can find here:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$HOME/.keras/keras.json
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you are a Windows user, you have to replace &lt;code&gt;$HOME&lt;/code&gt; with &lt;code&gt;%USERPROFILE%&lt;/code&gt;. The configuration file should look 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;image_data_format&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;channels_last&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;quot;epsilon&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1e-07&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;quot;floatx&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;float32&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;quot;backend&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;tensorflow&amp;quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can change the &lt;code&gt;backend&lt;/code&gt; field there to &lt;code&gt;&quot;theano&quot;&lt;/code&gt;, &lt;code&gt;&quot;tensorflow&quot;&lt;/code&gt; or &lt;code&gt;&quot;cntk&quot;&lt;/code&gt;, given that you have installed the backend on your machine. For more details check out the &lt;a href=&quot;https://keras.io/backend/&quot;&gt;Keras backends&lt;/a&gt; documentation.&lt;/p&gt;
&lt;p&gt;You might notice that we use &lt;code&gt;float32&lt;/code&gt; data in the configuration file. The reason for this is that neural networks are frequently used in GPUs, and the computational bottleneck is memory. By using 32 bit, we are able reduce the memory load and we do not lose too much information in the process.&lt;/p&gt;
&lt;h3 id=&quot;your-first-keras-model&quot;&gt;Your First Keras Model&lt;/h3&gt;
&lt;p&gt;Now you are finally ready to experiment with Keras. Keras supports two main types of models. You have the &lt;a href=&quot;https://keras.io/models/sequential/&quot;&gt;Sequential model API&lt;/a&gt; which you are going to see in use in this tutorial and the &lt;a href=&quot;https://keras.io/models/model/&quot;&gt;functional API&lt;/a&gt; which can do everything of the Sequential model but it can be also used for advanced models with complex network architectures.&lt;/p&gt;
&lt;p&gt;The Sequential model is a linear stack of layers, where you can use the large variety of available layers in Keras. The most common layer is the &lt;a href=&quot;https://keras.io/layers/core/#dense&quot;&gt;Dense&lt;/a&gt; layer which is your regular densely connected neural network layer with all the weights and biases that you are already familiar with.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see if we can achieve some improvement to our previous logistic regression model. You can use the &lt;code&gt;X_train&lt;/code&gt; and &lt;code&gt;X_test&lt;/code&gt; arrays that you built in our earlier example.&lt;/p&gt;
&lt;p&gt;Before we build our model, we need to know the input dimension of our feature vectors. This happens only in the first layer since the following layers can do automatic shape inference. In order to build the Sequential model, you can add layers one by one in order as follows:&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;keras.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&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;keras&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layers&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;input_dim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&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;# Number of features&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;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&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;model&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;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;input_dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relu&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;model&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;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sigmoid&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Using TensorFlow backend.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Before you can start with the training of the model, you need to configure the learning process. This is done with the &lt;code&gt;.compile()&lt;/code&gt; method. This method specifies the optimizer and the loss function.&lt;/p&gt;
&lt;p&gt;Additionally, you can add a list of metrics which can be later used for evaluation, but they do not influence the training. In this case, we want to use the binary cross entropy and the Adam optimizer you saw in the primer mentioned before. Keras also includes a handy &lt;code&gt;.summary()&lt;/code&gt; function to give an overview of the model and the number of parameters available for training:&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;model&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;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;binary_crossentropy&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;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;adam&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;metrics&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;accuracy&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;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Layer (type)                 Output Shape          Param #   &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_1 (Dense)              (None, 10)            17150     &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_2 (Dense)              (None, 1)             11        &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Total params: 17,161&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Trainable params: 17,161&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Non-trainable params: 0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You might notice that we have 8575 parameters for the first layer and another 6 in the next one. Where did those come from?&lt;/p&gt;
&lt;p&gt;See, we have 1714 dimensions for each feature vector, and then we have 5 nodes. We need weights for each feature dimension and each node which accounts for &lt;code&gt;1714 * 5 = 8570&lt;/code&gt; parameters, and then we have another 5 times an added bias for each node, which gets us the 8575 parameters. In the final node, we have another 5 weights and one bias, which gets us to 6 parameters.&lt;/p&gt;
&lt;p&gt;Neat! You are almost there. Now it is time to start your training with the &lt;code&gt;.fit()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Since the training in neural networks is an iterative process, the training won&amp;rsquo;t just stop after it is done. You have to specify the number of iterations you want the model to be training. Those completed iterations are commonly called &lt;strong&gt;epochs&lt;/strong&gt;. We want to run it for 100 epochs to be able to see how the training loss and accuracy are changing after each epoch.&lt;/p&gt;
&lt;p&gt;Another parameter you have to your selection is the &lt;strong&gt;batch size&lt;/strong&gt;. The batch size is responsible for how many samples we want to use in one epoch, which means how many samples are used in one forward/backward pass. This increases the speed of the computation as it need fewer epochs to run, but it also needs more memory, and the model may degrade with larger batch sizes. Since we have a small training set, we can leave this to a low batch size:&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;history&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&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;epochs&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;verbose&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;gp&quot;&gt;... &lt;/span&gt;                    &lt;span class=&quot;n&quot;&gt;validation_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_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&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;batch_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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you can use the &lt;code&gt;.evaluate()&lt;/code&gt; method to measure the accuracy of the model. You can do this both for the training data and testing data. We expect that the training data has a higher accuracy then for the testing data. Tee longer you would train a neural network, the more likely it is that it starts overfitting.&lt;/p&gt;
&lt;p&gt;Note that if you rerun the &lt;code&gt;.fit()&lt;/code&gt; method, you&amp;rsquo;ll start off with the computed weights from the previous training. Make sure to compile the model again before you start training the model again. Now let&amp;rsquo;s evaluate the accuracy 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;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;s2&quot;&gt;&amp;quot;Training Accuracy: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&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;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;s2&quot;&gt;&amp;quot;Testing Accuracy:  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Training Accuracy: 1.0000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing Accuracy:  0.7960&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can already see that the model was overfitting since it reached 100% accuracy for the training set. But this was expected since the number of epochs was fairly large for this model. However, the accuracy of the testing set has already surpassed our previous logistic Regression with BOW model, which is a great step further in terms of our progress.&lt;/p&gt;
&lt;p&gt;To make your life easier, you can use this little helper function to visualize the loss and accuracy for the training and testing data based on the &lt;a href=&quot;https://keras.io/callbacks/#history&quot;&gt;History&lt;/a&gt; callback. This callback, which is automatically applied to each Keras model, records the loss and additional &lt;a href=&quot;https://keras.io/metrics/&quot;&gt;metrics&lt;/a&gt; that can be added in the &lt;code&gt;.fit()&lt;/code&gt; method. In this case, we are only interested in the accuracy. This helper function employs the &lt;a href=&quot;https://matplotlib.org/&quot;&gt;matplotlib&lt;/a&gt; plotting library:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;plt&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;ggplot&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;plot_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;acc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val_acc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;val_acc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;loss&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val_loss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;val_loss&amp;#39;&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;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;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;plt&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;figsize&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;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subplot&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;plt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot&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;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Training acc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot&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;val_acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Validation acc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Training and validation accuracy&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;plt&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;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subplot&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;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot&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;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Training loss&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot&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;val_loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Validation loss&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Training and validation loss&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;plt&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;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To use this function, simply call &lt;code&gt;plot_history()&lt;/code&gt; with the collected accuracy and loss inside the &lt;code&gt;history&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;plot_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/loss-accuracy-baseline-model.ed95465579bd.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/loss-accuracy-baseline-model.ed95465579bd.png&quot; width=&quot;710&quot; height=&quot;320&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-baseline-model.ed95465579bd.png&amp;amp;w=177&amp;amp;sig=c7ed308861fabb5e27597af560a8b48c4e869a2b 177w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-baseline-model.ed95465579bd.png&amp;amp;w=355&amp;amp;sig=3980bd0d0e2a29eb61dc643d9e621d3e09fc54dd 355w, https://files.realpython.com/media/loss-accuracy-baseline-model.ed95465579bd.png 710w&quot; sizes=&quot;75vw&quot; alt=&quot;loss accuracy baseline model&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Accuracy and loss for baseline model&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;You can see that we have trained our model for too long since the training set reached 100% accuracy. A good way to see when the model starts overfitting is when the loss of the validation data starts rising again. This tends to be a good point to stop the model. You can see this around 20-40 epochs in this training.&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; When training neural networks, you should use a separate testing and validation set. What you would usually do is take the model with the highest validation accuracy and then test the model with the testing set.&lt;/p&gt;
&lt;p&gt;This makes sure that you don&amp;rsquo;t overfit the model. Using the validation set to choose the best model is a form of &lt;strong&gt;data leakage&lt;/strong&gt; (or &amp;ldquo;cheating&amp;rdquo;) to get to pick the result that produced the best test score out of hundreds of them. Data leakage happens when information outside the training data set is used in the model.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In this case, our testing and validation set are the same, since we have a smaller sample size. As we have covered before, (deep) neural networks perform best when you have a very large number of samples. In the next part, you&amp;rsquo;ll see a different way to represent words as vectors. This is a very exciting and powerful way to work with words where you&amp;rsquo;ll see how to represent words as dense vectors.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-word-embedding&quot;&gt;What Is a Word Embedding?&lt;/h2&gt;
&lt;p&gt;Text is considered a form of sequence data similar to time series data that you would have in weather data or financial data. In the previous BOW model, you have seen how to represent a whole sequence of words as a single feature vector. Now you will see how to represent each word as vectors. There are various ways to vectorize text, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Words represented by each word as a vector &lt;/li&gt;
&lt;li&gt;Characters represented by each character as a vector&lt;/li&gt;
&lt;li&gt;N-grams of words/characters represented as a vector (N-grams are overlapping groups of multiple succeeding words/characters in the text) &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this tutorial, you&amp;rsquo;ll see how to deal with representing words as vectors which is the common way to use text in neural networks. Two possible ways to represent a word as a vector are one-hot encoding and word embeddings.&lt;/p&gt;
&lt;h3 id=&quot;one-hot-encoding&quot;&gt;One-Hot Encoding&lt;/h3&gt;
&lt;p&gt;The first way to represent a word as a vector is by creating a so-called &lt;a href=&quot;https://en.wikipedia.org/wiki/One-hot&quot;&gt;one-hot&lt;/a&gt; encoding, which is simply done by taking a vector of the length of the vocabulary with an entry for each word in the corpus.&lt;/p&gt;
&lt;p&gt;In this way, you have for each word, given it has a spot in the vocabulary, a vector with zeros everywhere except for the corresponding spot for the word which is set to one. As you might imagine, this can become a fairly large vector for each word and it does not give any additional information like the relationship between words.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say you have a list of cities as in 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;cities&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;London&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Berlin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Berlin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;New York&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;London&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;cities&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;London&amp;#39;, &amp;#39;Berlin&amp;#39;, &amp;#39;Berlin&amp;#39;, &amp;#39;New York&amp;#39;, &amp;#39;London&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can use scikit-learn and the &lt;code&gt;LabelEncoder&lt;/code&gt; to encode the list of cities into categorical integer values like 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;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sklearn.preprocessing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LabelEncoder&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;encoder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LabelEncoder&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;city_labels&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit_transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cities&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;city_labels&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;array([1, 0, 0, 2, 1])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using this representation, you can use the &lt;code&gt;OneHotEncoder&lt;/code&gt; provided by scikit-learn to encode the categorical values we got before into a one-hot encoded numeric array. &lt;code&gt;OneHotEncoder&lt;/code&gt; expects each categorical value to be in a separate row, so you&amp;rsquo;ll need to reshape the array, then you can apply the encoder:&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;sklearn.preprocessing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OneHotEncoder&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;encoder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OneHotEncoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sparse&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;city_labels&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;city_labels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reshape&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;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;encoder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit_transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;city_labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;array([[0., 1., 0.],&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;       [1., 0., 0.],&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;       [1., 0., 0.],&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;       [0., 0., 1.],&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;       [0., 1., 0.]])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see that categorical integer value represents the position of the array which is &lt;code&gt;1&lt;/code&gt; and the rest is &lt;code&gt;0&lt;/code&gt;. This is often used when you have a categorical feature which you cannot represent as a numeric value but you still want to be able to use it in machine learning. One use case for this encoding is of course words in a text but it is most prominently used for categories. Such categories can be for example city, department, or other categories.&lt;/p&gt;
&lt;h3 id=&quot;word-embeddings&quot;&gt;Word Embeddings&lt;/h3&gt;
&lt;p&gt;This method represents words as dense word vectors (also called word embeddings) which are trained unlike the one-hot encoding which are hardcoded. This means that the word embeddings collect more information into fewer dimensions.&lt;/p&gt;
&lt;p&gt;Note that the word embeddings do not understand the text as a human would, but they rather map the statistical structure of the language used in the corpus. Their aim is to map semantic meaning into a geometric space. This geometric space is then called the &lt;strong&gt;embedding space&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This would map semantically similar words close on the embedding space like numbers or colors. If the embedding captures the relationship between words well, things like vector arithmetic should become possible. A famous example in this field of study is the ability to map &lt;a href=&quot;https://www.technologyreview.com/s/541356/king-man-woman-queen-the-marvelous-mathematics-of-computational-linguistics/&quot;&gt;King - Man + Woman = Queen&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;How can you get such a word embedding? You have two options for this. One way is to train your word embeddings during the training of your neural network. The other way is by using pretrained word embeddings which you can directly use in your model. There you have the option to either leave these word embeddings unchanged during training or you train them also. &lt;/p&gt;
&lt;p&gt;Now you need to tokenize the data into a format that can be used by the word embeddings. Keras offers a couple of convenience methods for &lt;a href=&quot;https://keras.io/preprocessing/text/&quot;&gt;text preprocessing&lt;/a&gt; and &lt;a href=&quot;https://keras.io/preprocessing/sequence/&quot;&gt;sequence preprocessing&lt;/a&gt; which you can employ to prepare your text.&lt;/p&gt;
&lt;p&gt;You can start by using the &lt;code&gt;Tokenizer&lt;/code&gt; utility class which can vectorize a text corpus into a list of integers. Each integer maps to a value in a dictionary that encodes the entire corpus, with the keys in the dictionary being the vocabulary terms themselves. You can add the parameter &lt;code&gt;num_words&lt;/code&gt;, which is responsible for setting the size of the vocabulary. The most common &lt;code&gt;num_words&lt;/code&gt; words will be then kept. I have the testing and training data prepared from the previous 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;keras.preprocessing.text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tokenizer&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;tokenizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_words&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit_on_texts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_train&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;X_train&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texts_to_sequences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_train&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;X_test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texts_to_sequences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_test&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;vocab_size&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;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Adding 1 because of reserved 0 index&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;sentences_train&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;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;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Of all the dishes, the salmon was the best, but all were great.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[11, 43, 1, 171, 1, 283, 3, 1, 47, 26, 43, 24, 22]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The indexing is ordered after the most common words in the text, which you can see by the word &lt;code&gt;the&lt;/code&gt; having the index &lt;code&gt;1&lt;/code&gt;. It is important to note that the index &lt;code&gt;0&lt;/code&gt; is reserved and is not assigned to any word. This zero index is used for padding, which I&amp;rsquo;ll introduce in a moment.&lt;/p&gt;
&lt;p&gt;Unknown words (words that are not in the vocabulary) are denoted in Keras with &lt;code&gt;word_count + 1&lt;/code&gt; since they can also hold some information. You can see the index of each word by taking a look at the &lt;code&gt;word_index&lt;/code&gt; dictionary of the &lt;code&gt;Tokenizer&lt;/code&gt; 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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;the&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;all&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;happy&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sad&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;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&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;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;the: 1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;all: 43&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;happy: 320&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sad: 450&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; Pay close attention to the difference between this technique and the &lt;code&gt;X_train&lt;/code&gt; that was produced by scikit-learn&amp;rsquo;s &lt;code&gt;CountVectorizer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;CountVectorizer&lt;/code&gt;, we had stacked vectors of word counts, and each vector was the same length (the size of the total corpus vocabulary). With Tokenizer, the resulting vectors equal the length of each text, and the numbers don&amp;rsquo;t denote counts, but rather correspond to the word values from the dictionary &lt;code&gt;tokenizer.word_index&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;One problem that we have is that each text sequence has in most cases different length of words. To counter this, you can use &lt;code&gt;pad_sequence()&lt;/code&gt; which simply pads the sequence of words with zeros. By default, it prepends zeros but we want to append them. Typically it does not matter whether you prepend or append zeros.&lt;/p&gt;
&lt;p&gt;Additionally you would want to add a &lt;code&gt;maxlen&lt;/code&gt; parameter to specify how long the sequences should be. This cuts sequences that exceed that number. In the following code, you can see how to pad sequences with Keras:&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;keras.preprocessing.sequence&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad_sequences&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;maxlen&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad_sequences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;maxlen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&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;X_test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad_sequences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;maxlen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&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;X_train&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;p&quot;&gt;:])&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[  1  10   3 282 739  25   8 208  30  64 459 230  13   1 124   5 231   8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  58   5  67   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   0   0   0   0   0   0   0   0   0   0]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first values represent the index in the vocabulary as you have learned from the previous examples. You can also see that the resulting feature vector contains mostly zeros, since you have a fairly short sentence. In the next part you will see how to work with word embeddings in Keras.&lt;/p&gt;
&lt;h3 id=&quot;keras-embedding-layer&quot;&gt;Keras Embedding Layer&lt;/h3&gt;
&lt;p&gt;Notice that, at this point, our data is still hardcoded. We have not told Keras to learn a new embedding space through successive tasks. Now you can use the &lt;a href=&quot;https://keras.io/layers/embeddings/&quot;&gt;Embedding Layer&lt;/a&gt; of Keras which takes the previously calculated integers and maps them to a dense vector of the embedding. You will need the following parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;input_dim&lt;/code&gt;:&lt;/strong&gt; the size of the vocabulary&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;output_dim&lt;/code&gt;:&lt;/strong&gt; the size of the dense vector&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;input_length&lt;/code&gt;:&lt;/strong&gt; the length of the sequence &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the &lt;code&gt;Embedding&lt;/code&gt; layer we have now a couple of options. One way would be to take the output of the embedding layer and plug it into a &lt;code&gt;Dense&lt;/code&gt; layer. In order to do this you have to add a &lt;code&gt;Flatten&lt;/code&gt; layer in between that prepares the sequential input for the &lt;code&gt;Dense&lt;/code&gt; layer:&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;keras.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;keras&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;embedding_dim&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;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Embedding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;output_dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;input_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Flatten&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relu&amp;#39;&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sigmoid&amp;#39;&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;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;adam&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;binary_crossentropy&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;metrics&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;accuracy&amp;#39;&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;summary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Layer (type)                 Output Shape              Param #   &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;embedding_8 (Embedding)      (None, 100, 50)           87350     &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;flatten_3 (Flatten)          (None, 5000)              0         &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_13 (Dense)             (None, 10)                50010     &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_14 (Dense)             (None, 1)                 11        &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Total params: 137,371&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Trainable params: 137,371&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Non-trainable params: 0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can now see that we have 87350 new parameters to train. This number comes from &lt;code&gt;vocab_size&lt;/code&gt; times the &lt;code&gt;embedding_dim&lt;/code&gt;. These weights of the embedding layer are initialized with random weights and are then adjusted through backpropagation during training. This model takes the words as they come in the order of the sentences as input vectors. You can train it 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;n&quot;&gt;history&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;epochs&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;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;validation_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_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;batch_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;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Training Accuracy: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Testing Accuracy:  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plot_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;Training Accuracy: 0.5100&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing Accuracy:  0.4600&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/loss-accuracy-first-model.95140204b674.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/loss-accuracy-first-model.95140204b674.png&quot; width=&quot;716&quot; height=&quot;320&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-first-model.95140204b674.png&amp;amp;w=179&amp;amp;sig=acd125420a75fcae19e2781f5dd845de803cd824 179w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-first-model.95140204b674.png&amp;amp;w=358&amp;amp;sig=b8b2c5c99b088a0a66afaf5cc5b674cd61dcf8c0 358w, https://files.realpython.com/media/loss-accuracy-first-model.95140204b674.png 716w&quot; sizes=&quot;75vw&quot; alt=&quot;loss accuracy first model&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Accuracy and loss for first model&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;This is typically a not very reliable way to work with sequential data as you can see in the performance. When working with sequential data you want to focus on methods that look at local and sequential information instead of absolute positional information. &lt;/p&gt;
&lt;p&gt;Another way to work with embeddings is by using a &lt;code&gt;MaxPooling1D&lt;/code&gt;/&lt;code&gt;AveragePooling1D&lt;/code&gt; or a &lt;code&gt;GlobalMaxPooling1D&lt;/code&gt;/&lt;code&gt;GlobalAveragePooling1D&lt;/code&gt; layer after the embedding. You can think of the pooling layers as a way to &lt;a href=&quot;https://en.wikipedia.org/wiki/Decimation_(signal_processing)&quot;&gt;downsample&lt;/a&gt; (a way to reduce the size of) the incoming feature vectors.&lt;/p&gt;
&lt;p&gt;In the case of max pooling you take the maximum value of all features in the pool for each feature dimension. In the case of average pooling you take the average, but max pooling seems to be more commonly used as it highlights large values.&lt;/p&gt;
&lt;p&gt;Global max/average pooling takes the maximum/average of all features whereas in the other case you have to define the pool size. Keras has again its own layer that you can add in the sequential 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;keras.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;keras&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;embedding_dim&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;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Embedding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;output_dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;input_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GlobalMaxPool1D&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relu&amp;#39;&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sigmoid&amp;#39;&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;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;adam&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;binary_crossentropy&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;metrics&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;accuracy&amp;#39;&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;summary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Layer (type)                 Output Shape              Param #   &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;embedding_9 (Embedding)      (None, 100, 50)           87350     &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;global_max_pooling1d_5 (Glob (None, 50)                0         &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_15 (Dense)             (None, 10)                510       &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_16 (Dense)             (None, 1)                 11        &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Total params: 87,871&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Trainable params: 87,871&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Non-trainable params: 0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The procedure for training does not change:&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;history&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;epochs&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;span class=&quot;n&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;validation_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_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;batch_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;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Training Accuracy: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Testing Accuracy:  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plot_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be as follows:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Training Accuracy: 1.0000
Testing Accuracy:  0.8050
&lt;/pre&gt;&lt;/div&gt;

&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/loss-accurcay-max-pooling.5f0987b44c65.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/loss-accurcay-max-pooling.5f0987b44c65.png&quot; width=&quot;716&quot; height=&quot;320&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accurcay-max-pooling.5f0987b44c65.png&amp;amp;w=179&amp;amp;sig=ff69cc655a4ffa3e61740f28fe28701f1190905e 179w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accurcay-max-pooling.5f0987b44c65.png&amp;amp;w=358&amp;amp;sig=5082ad2dbffb2803f5427aa0c802b430a083bb83 358w, https://files.realpython.com/media/loss-accurcay-max-pooling.5f0987b44c65.png 716w&quot; sizes=&quot;75vw&quot; alt=&quot;loss accurcay max pooling&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Accuracy and loss for max pooling model&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;You can already see some improvements in our models. Next you&amp;rsquo;ll see how we can employ pretrained word embeddings and if they help us with our model.&lt;/p&gt;
&lt;h3 id=&quot;using-pretrained-word-embeddings&quot;&gt;Using Pretrained Word Embeddings&lt;/h3&gt;
&lt;p&gt;We just saw an example of jointly learning word embeddings incorporated into the larger model that we want to solve.&lt;/p&gt;
&lt;p&gt;An alternative is to use a precomputed embedding space that utilizes a much larger corpus. It is possible to precompute word embeddings by simply training them on a large corpus of text. Among the most popular methods are &lt;a href=&quot;https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf&quot;&gt;Word2Vec&lt;/a&gt; developed by Google and &lt;a href=&quot;https://nlp.stanford.edu/projects/glove/&quot;&gt;GloVe&lt;/a&gt; (Global Vectors for Word Representation) developed by the Stanford NLP Group.&lt;/p&gt;
&lt;p&gt;Note that those are different approaches with the same goal. Word2Vec achieves this by employing neural networks and GloVe achieves this with a co-occurrence matrix and by using matrix factorization. In both cases you are dealing with dimensionality reduction, but Word2Vec is more accurate and GloVe is faster to compute.&lt;/p&gt;
&lt;p&gt;In this tutorial, you&amp;rsquo;ll see how to work with the GloVe word embeddings from the Stanford NLP Group as their size is more manageable than the Word2Vec word embeddings provided by Google. Go ahead and download the 6B (trained on 6 billion words) word embeddings from &lt;a href=&quot;http://nlp.stanford.edu/data/glove.6B.zip&quot;&gt;here&lt;/a&gt; (822 MB).&lt;/p&gt;
&lt;p&gt;You can find other word embeddings also on the main &lt;a href=&quot;https://nlp.stanford.edu/projects/glove/&quot;&gt;GloVe&lt;/a&gt; page. You can find the pretrained Word2Vec embeddings by Google &lt;a href=&quot;https://code.google.com/archive/p/word2vec/&quot;&gt;here&lt;/a&gt;. If you want to train your own word embeddings, you can do so efficiently with the &lt;a href=&quot;https://radimrehurek.com/gensim/index.html&quot;&gt;gensim&lt;/a&gt; Python package which uses Word2Vec for calculation. More details on how to do this &lt;a href=&quot;https://radimrehurek.com/gensim/models/word2vec.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now that we got you covered, you can start using the word embeddings in your models. You can see in the next example how you can load the embedding matrix. Each line in the file starts with the word and is followed by the embedding vector for the particular word.&lt;/p&gt;
&lt;p&gt;This is a large file with 400000 lines, with each line representing a word followed by its vector as a stream of floats. For example, here are the first 50 characters of the first 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; head -n &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; data/glove_word_embeddings/glove.6B.50d.txt &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; cut -c-50
&lt;span class=&quot;go&quot;&gt;    the 0.418 0.24968 -0.41242 0.1217 0.34527 -0.04445&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since you don&amp;rsquo;t need all words, you can focus on only the words that we have in our vocabulary. Since we have only a limited number of words in our vocabulary, we can skip most of the 40000 words in the pretrained word embeddings:&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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_embedding_matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vocab_size&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;word_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Adding again 1 because of reserved 0 index&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;embedding_matrix&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;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embedding_dim&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;filepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;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;line&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;n&quot;&gt;word&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;vector&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;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;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word_index&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;n&quot;&gt;word_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; 
                &lt;span class=&quot;n&quot;&gt;embedding_matrix&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;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;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtype&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;float32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embedding_dim&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;embedding_matrix&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can use this function now to retrieve the embedding matrix:&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;embedding_dim&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embedding_matrix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_embedding_matrix&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;s1&quot;&gt;&amp;#39;data/glove_word_embeddings/glove.6B.50d.txt&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;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wonderful! Now you are ready to use the embedding matrix in training. Let&amp;rsquo;s go ahead and use the previous network with global max pooling and see if we can improve this model. When you use pretrained word embeddings you have the choice to either allow the embedding to be updated during training or only use the resulting embedding vectors as they are.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s have a quick look how many of the embedding vectors are nonzero:&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;nonzero_elements&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;count_nonzero&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;count_nonzero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embedding_matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;axis&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;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nonzero_elements&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;0.9507727532913566&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means 95.1% of the vocabulary is covered by the pretrained model, which is a good coverage of our vocabulary. Let&amp;rsquo;s have a look at the performance when using the &lt;code&gt;GlobalMaxPool1D&lt;/code&gt; layer:&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;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Embedding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;weights&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;embedding_matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;input_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;trainable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;model&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;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GlobalMaxPool1D&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relu&amp;#39;&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sigmoid&amp;#39;&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;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;adam&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;binary_crossentropy&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;metrics&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;accuracy&amp;#39;&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;summary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Layer (type)                 Output Shape              Param #   &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;embedding_10 (Embedding)     (None, 100, 50)           87350     &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;global_max_pooling1d_6 (Glob (None, 50)                0         &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_17 (Dense)             (None, 10)                510       &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_18 (Dense)             (None, 1)                 11        &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Total params: 87,871&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Trainable params: 521&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Non-trainable params: 87,350&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&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;n&quot;&gt;history&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;epochs&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;span class=&quot;n&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;validation_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_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;batch_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;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Training Accuracy: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Testing Accuracy:  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plot_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;Training Accuracy: 0.7500&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing Accuracy:  0.6950&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/loss-accuracy-embedding-untrained.230bc90c5536.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/loss-accuracy-embedding-untrained.230bc90c5536.png&quot; width=&quot;716&quot; height=&quot;320&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-embedding-untrained.230bc90c5536.png&amp;amp;w=179&amp;amp;sig=47f960f0061dc8aee897aefe881e17c62f42c15f 179w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-embedding-untrained.230bc90c5536.png&amp;amp;w=358&amp;amp;sig=7432b2cee852d971be6f79d561224467e0a83e0d 358w, https://files.realpython.com/media/loss-accuracy-embedding-untrained.230bc90c5536.png 716w&quot; sizes=&quot;75vw&quot; alt=&quot;loss accuracy embedding untrained&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Accuracy and loss for untrained word embeddings&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Since the word embeddings are not additionally trained, it is expected to be lower. But let&amp;rsquo;s now see how this performs if we allow the embedding to be trained by using &lt;code&gt;trainable=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;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Embedding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;weights&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;embedding_matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;input_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;trainable&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;model&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;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GlobalMaxPool1D&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relu&amp;#39;&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sigmoid&amp;#39;&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;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;adam&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;binary_crossentropy&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;metrics&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;accuracy&amp;#39;&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;summary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Layer (type)                 Output Shape              Param #   &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;embedding_11 (Embedding)     (None, 100, 50)           87350     &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;global_max_pooling1d_7 (Glob (None, 50)                0         &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_19 (Dense)             (None, 10)                510       &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_20 (Dense)             (None, 1)                 11        &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Total params: 87,871&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Trainable params: 87,871&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Non-trainable params: 0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&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;n&quot;&gt;history&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;epochs&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;span class=&quot;n&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;validation_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_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;batch_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;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Training Accuracy: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Testing Accuracy:  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plot_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;Training Accuracy: 1.0000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing Accuracy:  0.8250&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/loss-accuracy-embedding-trained.2e33069d7103.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/loss-accuracy-embedding-trained.2e33069d7103.png&quot; width=&quot;710&quot; height=&quot;320&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-embedding-trained.2e33069d7103.png&amp;amp;w=177&amp;amp;sig=2b5d16ea7d47ee9271ad9dd945c2dea91898ed11 177w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-embedding-trained.2e33069d7103.png&amp;amp;w=355&amp;amp;sig=1b75a904db4ffd064cb02d4f36304607622f9535 355w, https://files.realpython.com/media/loss-accuracy-embedding-trained.2e33069d7103.png 710w&quot; sizes=&quot;75vw&quot; alt=&quot;loss accuracy embedding trained&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Accuracy and Loss for pretrained word embeddings&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;You can see that it is most effective to allow the embeddings to be trained. When dealing with large training sets it can boost the training process to be much faster than without. In our case it seemed to help but not by much. This does not have to be because of pretrained word embeddings.&lt;/p&gt;
&lt;p&gt;Now it is time to focus on a more advanced neural network model to see if it is possible to boost the model and give it the leading edge over the previous models.&lt;/p&gt;
&lt;h2 id=&quot;convolutional-neural-networks-cnn&quot;&gt;Convolutional Neural Networks (CNN)&lt;/h2&gt;
&lt;p&gt;Convolutional neural networks or also called &lt;strong&gt;convnets&lt;/strong&gt; are one of the most exciting developments in machine learning in recent years.&lt;/p&gt;
&lt;p&gt;They have revolutionized image classification and computer vision by being able to extract features from images and using them in neural networks. The properties that made them useful in image processing makes them also handy for sequence processing. You can imagine a CNN as a specialized neural network that is able to detect specific patterns.&lt;/p&gt;
&lt;p&gt;If it is just another neural network, what differentiates it from what you have previously learned? &lt;/p&gt;
&lt;p&gt;A CNN has hidden layers which are called convolutional layers. When you think of images, a computer has to deal with a two dimensional matrix of numbers and therefore you need some way to detect features in this matrix. These convolutional layers are able to detect edges, corners and other kinds of textures which makes them such a special tool. The convolutional layer consists of multiple filters which are slid across the image and are able to detect specific features.&lt;/p&gt;
&lt;p&gt;This is the very core of the technique, the mathematical process of &lt;a href=&quot;https://en.wikipedia.org/wiki/Convolution&quot;&gt;convolution&lt;/a&gt;. With each convolutional layer the network is able to detect more complex patterns. In the &lt;a href=&quot;https://distill.pub/2017/feature-visualization/&quot;&gt;Feature Visualization&lt;/a&gt; by Chris Olah you can get a good intuition what these features can look like.&lt;/p&gt;
&lt;p&gt;When you are working with sequential data, like text, you work with one dimensional convolutions, but the idea and the application stays the same. You still want to pick up on patterns in the sequence which become more complex with each added convolutional layer.&lt;/p&gt;
&lt;p&gt;In the next figure you can see how such a convolution works. It starts by taking a patch of input features with the size of the filter kernel. With this patch you take the dot product of the multiplied weights of the filter. The one dimensional convnet is invariant to translations, which means that certain sequences can be recognized at a different position. This can be helpful for certain patterns in the text:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/njanakiev-1d-convolution.d7afddde2776.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/njanakiev-1d-convolution.d7afddde2776.png&quot; width=&quot;1280&quot; height=&quot;720&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/njanakiev-1d-convolution.d7afddde2776.png&amp;amp;w=320&amp;amp;sig=a8964821d9642bb6799b2b0924f61df09e6d5847 320w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/njanakiev-1d-convolution.d7afddde2776.png&amp;amp;w=640&amp;amp;sig=35c816a8827ff87b4147b9f6a91041f5ca539a60 640w, https://files.realpython.com/media/njanakiev-1d-convolution.d7afddde2776.png 1280w&quot; sizes=&quot;75vw&quot; alt=&quot;one dimensional convolution&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;1D Convolution (&lt;a href=&quot;https://realpython.com/asins/1617294438/&quot; target=&quot;_blank&quot;&gt;Image source&lt;/a&gt;)&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Now let&amp;rsquo;s have a look how you can use this network in Keras. Keras offers again various &lt;a href=&quot;https://keras.io/layers/convolutional/&quot;&gt;Convolutional layers&lt;/a&gt; which you can use for this task. The layer you&amp;rsquo;ll need is the &lt;code&gt;Conv1D&lt;/code&gt; layer. This layer has again various parameters to choose from. The ones you are interested in for now are the number of filters, the kernel size, and the activation function. You can add this layer in between the &lt;code&gt;Embedding&lt;/code&gt; layer and the &lt;code&gt;GlobalMaxPool1D&lt;/code&gt; layer:&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;embedding_dim&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;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequential&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Embedding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conv1D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relu&amp;#39;&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GlobalMaxPooling1D&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relu&amp;#39;&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sigmoid&amp;#39;&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;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;adam&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;binary_crossentropy&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;metrics&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;accuracy&amp;#39;&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;summary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Layer (type)                 Output Shape              Param #   &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;embedding_13 (Embedding)     (None, 100, 100)          174700    &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;conv1d_2 (Conv1D)            (None, 96, 128)           64128     &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;global_max_pooling1d_9 (Glob (None, 128)               0         &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_23 (Dense)             (None, 10)                1290      &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dense_24 (Dense)             (None, 1)                 11        &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=================================================================&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Total params: 240,129&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Trainable params: 240,129&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Non-trainable params: 0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;_________________________________________________________________&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;n&quot;&gt;history&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;epochs&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;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;validation_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_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;batch_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;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Training Accuracy: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accuracy&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;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;Testing Accuracy:  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&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;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plot_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result will be 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;go&quot;&gt;Training Accuracy: 1.0000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing Accuracy:  0.7700&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/loss-accuracy-convolution-model.35563e05ba91.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/loss-accuracy-convolution-model.35563e05ba91.png&quot; width=&quot;710&quot; height=&quot;320&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-convolution-model.35563e05ba91.png&amp;amp;w=177&amp;amp;sig=75e6adea5972607dde860dc56fdb8c7341c88b5d 177w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/loss-accuracy-convolution-model.35563e05ba91.png&amp;amp;w=355&amp;amp;sig=e05ca96932078796a4734cb122de49122b69551b 355w, https://files.realpython.com/media/loss-accuracy-convolution-model.35563e05ba91.png 710w&quot; sizes=&quot;75vw&quot; alt=&quot;loss accuracy convolution model&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Accuracy and loss for convolutional neural network&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;You can see that 80% accuracy seems to be tough hurdle to overcome with this data set and a CNN might not be well equipped. The reason for such a plateau might be that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are not enough training samples&lt;/li&gt;
&lt;li&gt;The data you have does not generalize well&lt;/li&gt;
&lt;li&gt;Missing focus on tweaking the hyperparameters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CNNs work best with large training sets where they are able to find generalizations where a simple model like logistic regression won&amp;rsquo;t be able.&lt;/p&gt;
&lt;h2 id=&quot;hyperparameters-optimization&quot;&gt;Hyperparameters Optimization&lt;/h2&gt;
&lt;p&gt;One crucial steps of deep learning and working with neural networks is &lt;a href=&quot;https://en.wikipedia.org/wiki/Hyperparameter_optimization&quot;&gt;hyperparameter optimization&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As you saw in the models that we have used so far, even with simpler ones, you had a large number of parameters to tweak and choose from. Those parameters are called hyperparameters. This is the most time consuming part of machine learning and sadly there are no one-fits-all solutions ready.&lt;/p&gt;
&lt;p&gt;When you have a look at the competitions on &lt;a href=&quot;http://kaggle.com/&quot;&gt;Kaggle&lt;/a&gt;, one of the largest places to compete against other fellow data scientists, you can see that many of the winning teams and models have gone through a lot of tweaking and experimenting until they reached their prime. So don&amp;rsquo;t get discouraged when it gets tough and you reach a plateau, but rather think about the ways you could optimize the model or the data.&lt;/p&gt;
&lt;p&gt;One popular method for hyperparameter optimization is &lt;a href=&quot;https://en.wikipedia.org/wiki/Hyperparameter_optimization#Grid_search&quot;&gt;grid search&lt;/a&gt;. What this method does is it takes lists of parameters and it runs the model with each parameter combination that it can find. It is the most thorough way but also the most computationally heavy way to do this. Another common way, &lt;strong&gt;random search&lt;/strong&gt;, which you&amp;rsquo;ll see in action here, simply takes random combinations of parameters.&lt;/p&gt;
&lt;p&gt;In order to apply random search with Keras, you will need to use the &lt;a href=&quot;https://keras.io/scikit-learn-api/&quot;&gt;KerasClassifier&lt;/a&gt; which serves as a wrapper for the &lt;a href=&quot;http://scikit-learn.org/&quot;&gt;scikit-learn API&lt;/a&gt;. With this wrapper you are able to use the various tools available with scikit-learn like &lt;a href=&quot;http://scikit-learn.org/stable/modules/cross_validation.html&quot;&gt;cross-validation&lt;/a&gt;. The class that you need is &lt;a href=&quot;http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.RandomizedSearchCV.html&quot;&gt;RandomizedSearchCV&lt;/a&gt; which implements random search with cross-validation. Cross-validation is a way to validate the model and take the whole data set and separate it into multiple testing and training data sets. &lt;/p&gt;
&lt;p&gt;There are various types of cross-validation. One type is the &lt;strong&gt;k-fold cross-validation&lt;/strong&gt; which you&amp;rsquo;ll see in this example. In this type the data set is partitioned into &lt;em&gt;k&lt;/em&gt; equal sized sets where one set is used for testing and the rest of the partitions are used for training. This enables you to run &lt;em&gt;k&lt;/em&gt; different runs, where each partition is once used as a testing set. So, the higher &lt;em&gt;k&lt;/em&gt; is the more accurate the model evaluation is, but the smaller each testing set is.&lt;/p&gt;
&lt;p&gt;First step for &lt;code&gt;KerasClassifier&lt;/code&gt; is to have a function that creates a Keras model. We will use the previous model, but we will allow various parameters to be set for the hyperparameter optimization:&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_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxlen&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;Sequential&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Embedding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conv1D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relu&amp;#39;&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GlobalMaxPooling1D&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relu&amp;#39;&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;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&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;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sigmoid&amp;#39;&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;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;adam&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;binary_crossentropy&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;metrics&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;accuracy&amp;#39;&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;model&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, you want to define the parameter grid that you want to use in training. This consists of a dictionary with each parameters named as in the previous function. The number of spaces on the grid is &lt;code&gt;3 * 3 * 1 * 1 * 1&lt;/code&gt;, where each of those numbers is the number of different choices for a given parameter.&lt;/p&gt;
&lt;p&gt;You can see how this could get computationally expensive very quickly, but luckily both grid search and random search are embarrassingly parallel, and the classes come with an &lt;code&gt;n_jobs&lt;/code&gt; parameter that lets you test grid spaces in parallel. The parameter grid is initialized with the following dictionary:&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;param_grid&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;num_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;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&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;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
                  &lt;span class=&quot;n&quot;&gt;embedding_dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;maxlen&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;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you are already ready to start running the random search. In this example we iterate over each data set and then you want to preprocess the data in the same way as previously. Afterwards you take the previous function and add it to the &lt;code&gt;KerasClassifier&lt;/code&gt; wrapper class including the number of epochs.&lt;/p&gt;
&lt;p&gt;The resulting instance and the parameter grid are then used as the estimator in the &lt;code&gt;RandomSearchCV&lt;/code&gt; class. Additionally, you can choose the number of folds in the k-folds cross-validation, which is in this case 4. You have seen most of the code in this snippet before in our previous examples. Besides the &lt;code&gt;RandomSearchCV&lt;/code&gt; and &lt;code&gt;KerasClassifier&lt;/code&gt;, I have added a little block of code handling the evaluation:&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;keras.wrappers.scikit_learn&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KerasClassifier&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sklearn.model_selection&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomizedSearchCV&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Main settings&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;epochs&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;n&quot;&gt;embedding_dim&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;n&quot;&gt;maxlen&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;n&quot;&gt;output_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;data/output.txt&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Run grid search for each source (yelp, amazon, imdb)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&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;source&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;Running grid search for data set :&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;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sentences&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sentence&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;values&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;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;label&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;values&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Train-test split&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sentences_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentences_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;train_test_split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sentences&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;test_size&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;random_state&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;c1&quot;&gt;# Tokenize words&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_words&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;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit_on_texts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texts_to_sequences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texts_to_sequences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentences_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Adding 1 because of reserved 0 index&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vocab_size&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;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Pad sequences with zeros&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad_sequences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;maxlen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad_sequences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;o&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;n&quot;&gt;maxlen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Parameter grid for grid search&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param_grid&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;num_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;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&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;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vocab_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;embedding_dim&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;embedding_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;maxlen&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;maxlen&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;KerasClassifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;build_fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;epochs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;epochs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;batch_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;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;grid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RandomizedSearchCV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;estimator&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;param_distributions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                              &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&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;n_iter&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;grid_result&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;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Evaluate testing set&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;test_accuracy&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;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Save and evaluate results&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;prompt&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;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;finished &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{source}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;; write to file and proceed? [y/n]&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;prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&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;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;true&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;yes&amp;#39;&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;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;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Running &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; data set&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Best Accuracy : &amp;#39;&lt;/span&gt;
             &lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Test Accuracy : &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{:.4f}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\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;span class=&quot;n&quot;&gt;output_string&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;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;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;grid_result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best_score_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;grid_result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best_params_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;test_accuracy&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;output_string&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;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This takes a while which is a perfect chance to go outside to get some fresh air or even go on a hike, depending on how many models you want to run. Let&amp;rsquo;s take a look what we have got:&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;Running amazon data set&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Best Accuracy : 0.8122&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;vocab_size&amp;#39;: 4603, &amp;#39;num_filters&amp;#39;: 64, &amp;#39;maxlen&amp;#39;: 100, &amp;#39;kernel_size&amp;#39;: 5, &amp;#39;embedding_dim&amp;#39;: 50}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Test Accuracy : 0.8457&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Running imdb data set&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Best Accuracy : 0.8161&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;vocab_size&amp;#39;: 4603, &amp;#39;num_filters&amp;#39;: 128, &amp;#39;maxlen&amp;#39;: 100, &amp;#39;kernel_size&amp;#39;: 5, &amp;#39;embedding_dim&amp;#39;: 50}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Test Accuracy : 0.8210&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Running yelp data set&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Best Accuracy : 0.8127&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;vocab_size&amp;#39;: 4603, &amp;#39;num_filters&amp;#39;: 64, &amp;#39;maxlen&amp;#39;: 100, &amp;#39;kernel_size&amp;#39;: 7, &amp;#39;embedding_dim&amp;#39;: 50}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Test Accuracy : 0.8384&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Interesting! For some reason the testing accuracy is higher than the training accuracy which might be because there is a large variance in the scores during cross-validation. We can see that we were still not able to break much through the dreaded 80%, which seems to be a natural limit for this data with its given size. Remember that we have a small data set and convolutional neural networks tend to perform the best with large data sets.&lt;/p&gt;
&lt;p&gt;Another method for CV is the &lt;strong&gt;nested cross-validation&lt;/strong&gt; (shown &lt;a href=&quot;http://scikit-learn.org/stable/tutorial/statistical_inference/model_selection.html#grid-search&quot;&gt;here&lt;/a&gt;) which is used when the hyperparameters also need to be optimized. This is used because the resulting non-nested CV model has a bias toward the data set which can lead to an overly optimistic score. You see, when doing hyperparameter optimization as we did in the previous example, we are picking the best hyperparameters for that specific training set but this does not mean that these hyperparameters generalize the best.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There you have it: you have learned how to work with text classification with Keras, and we have gone from a bag-of-words model with logistic regression to increasingly more advanced methods leading to convolutional neural networks.&lt;/p&gt;
&lt;p&gt;You should be now familiar with word embeddings, why they are useful, and also how to use pretrained word embeddings for your training. You have also learned how to work with neural networks and how to use hyperparameter optimization to squeeze more performance out of your model.&lt;/p&gt;
&lt;p&gt;One big topic which we have not covered here left for another time was &lt;a href=&quot;https://en.wikipedia.org/wiki/Recurrent_neural_network&quot;&gt;recurrent neural networks&lt;/a&gt;, more specifically &lt;a href=&quot;https://en.wikipedia.org/wiki/Long_short-term_memory&quot;&gt;LSTM&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Gated_recurrent_unit&quot;&gt;GRU&lt;/a&gt;. Those are other powerful and popular tools to work with sequential data like text or time series. Other interesting developments are currently in neural networks that employ &lt;a href=&quot;https://distill.pub/2016/augmented-rnns/&quot;&gt;attention&lt;/a&gt; which are under active research and seem to be a promising next step since LSTM tend to be heavy on the computation.&lt;/p&gt;
&lt;p&gt;You have now an understanding of a crucial cornerstone in natural language processing which you can use for text classification of all sorts. Sentiment analysis is the most prominent example for this, but this includes many other applications such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spam detection in emails&lt;/li&gt;
&lt;li&gt;Automatic tagging of texts&lt;/li&gt;
&lt;li&gt;Categorization of news articles with predefined topics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can use this knowledge and the models that you have trained on an advanced project as in this &lt;a href=&quot;https://realpython.com/twitter-sentiment-python-docker-elasticsearch-kibana/&quot;&gt;tutorial&lt;/a&gt; to employ sentiment analysis on a continuous stream of twitter data with Kibana and Elasticsearch. You could also combine sentiment analysis or text classification with speech recognition like in this handy &lt;a href=&quot;https://realpython.com/python-speech-recognition/&quot;&gt;tutorial&lt;/a&gt; using the SpeechRecognition library in Python.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;
&lt;p&gt;If you want to delve deeper into the various topics from this article you can take a look at these links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.sciencemag.org/news/2018/05/ai-researchers-allege-machine-learning-alchemy?es_p=6976017&quot;&gt;AI researchers allege that machine learning is alchemy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/1705.08807&quot;&gt;When Will AI Exceed Human Performance? Evidence from AI Experts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/keras-team/keras/tree/master/examples&quot;&gt;Keras Code Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://colah.github.io/posts/2014-07-NLP-RNNs-Representations/&quot;&gt;Deep Learning, NLP, and Representations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf&quot;&gt;Word2Vec Paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nlp.stanford.edu/pubs/glove.pdf&quot;&gt;GloVe Paper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  

</feed>
