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

  
    <entry>
      <title>The Ultimate List of Data Science Podcasts</title>
      <id>https://realpython.com/data-science-podcasts/</id>
      <link href="https://realpython.com/data-science-podcasts/"/>
      <updated>2019-02-06T06:00:00+00:00</updated>
      <summary>The ultimate list of data science podcasts! Over a dozen shows that discuss topics in big data, data analysis, statistics, machine learning, and artificial intelligence.</summary>
      <content type="html">
        &lt;p&gt;Podcasts are a great way to immerse yourself in an industry, especially when it comes to data science. The field moves extremely quickly, and it can be difficult to keep up with all the new developments happening each week!&lt;/p&gt;
&lt;p&gt;Take advantage of those times in the day when your body is busy, but your mind is free: when you&amp;rsquo;re commuting to work, exercising at the gym, or cleaning up around the house. These are optimal times to engage your brain in learning something new and ensure you&amp;rsquo;re staying at the top of your field.&lt;/p&gt;
&lt;p&gt;There are dozens of data science podcasts out there, covering everything from &lt;a href=&quot;https://realpython.com/python-windows-machine-learning-setup/&quot;&gt;machine learning&lt;/a&gt; and artificial intelligence to big data analytics. We hope this will be a great resource for you to find useful, informative, and engaging shows.&lt;/p&gt;
&lt;p&gt;Get ready to dive 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-numpy-learning-guide&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a free NumPy Resources Guide&lt;/a&gt; that points you to the best tutorials, videos, and books for improving your NumPy skills.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;active-data-science-podcasts&quot;&gt;Active Data Science Podcasts&lt;/h2&gt;
&lt;p&gt;As of this writing, these data science podcasts are active and still in production. Start deep in the archives and work your way up, or jump right into the latest episode!&lt;/p&gt;
&lt;h3 id=&quot;data-skeptic&quot;&gt;Data Skeptic&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;https://dataskeptic.com/&quot;&gt;https://dataskeptic.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/dataskeptic&quot;&gt;@dataskeptic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;https://dataskeptic.libsyn.com/rss&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/data-skeptic/id890348705?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/6r4mt-34d76/Data+Skeptic&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/data-skeptic&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/art.72513911b135.png&quot; width=&quot;840&quot; height=&quot;840&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/art.72513911b135.png&amp;amp;w=210&amp;amp;sig=9b8c571eff4f70a305421163cadf0e67e9b54979 210w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/art.72513911b135.png&amp;amp;w=420&amp;amp;sig=13c23bdaa8d2b1565ccc0d8c327b396f3695c834 420w, https://files.realpython.com/media/art.72513911b135.png 840w&quot; sizes=&quot;75vw&quot; alt=&quot;Data Skeptic Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Data Skeptic is one of the best-known data science podcasts. This weekly show explores topics in data science, statistics, machine learning and artificial intelligence.&lt;/p&gt;
&lt;p&gt;Hosted by Kyle Polich, the show is going strong with over 200 episodes for listeners to dive into. Recently, the show has released series of themed episodes that revolve around a larger topic in the data science world, like fake news.&lt;/p&gt;
&lt;p&gt;The episodes alternate between interviews with industry professionals and minisodes that explain high-level data science concepts.&lt;/p&gt;
&lt;p&gt;The minisodes are co-hosted by Linh Da Tran, who talks with Kyle about data science topics, like natural language processing and k-means clustering. Listeners gain a better understanding of the topic as the hosts talk through it.&lt;/p&gt;
&lt;h3 id=&quot;linear-digressions&quot;&gt;Linear Digressions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;http://lineardigressions.com&quot;&gt;http://lineardigressions.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/lindigressions?lang=en&quot;&gt;@LinDigressions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;http://lineardigressions.com/episodes?format=RSS&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/linear-digressions/id941219323?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/jeif2-34d14/Linear-Digressions-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/linear-digressions-74115&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-linear-digressions.a4dc149eef09.jpg&quot; width=&quot;1440&quot; height=&quot;1440&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-linear-digressions.a4dc149eef09.jpg&amp;amp;w=360&amp;amp;sig=6f7ab738b2656818658684a1a04690bc9b890f90 360w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-linear-digressions.a4dc149eef09.jpg&amp;amp;w=720&amp;amp;sig=b7be4da8fd05ab4eab509d74073beee203e2694c 720w, https://files.realpython.com/media/podcast-logo-linear-digressions.a4dc149eef09.jpg 1440w&quot; sizes=&quot;75vw&quot; alt=&quot;Linear Digressions Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Katie Malone and Ben Jaffe host Linear Digressions, a weekly podcast that explores recent developments in data science, machine learning, and artificial intelligence. The hosts are good friends, and their rapport makes each episode very accessible and easy to understand.&lt;/p&gt;
&lt;p&gt;As of this writing, there are over 100 episodes for listeners to dive into. Each episode clocks in at around half an hour, making it a breeze to gain a quick understanding of the topic at hand.&lt;/p&gt;
&lt;p&gt;Katie and Ben do a great job at distilling a complex technical topic down to its fundamentals. In just a few short minutes, they demystify neural networks, autoencoders, the Fourier transform, and more.&lt;/p&gt;
&lt;h3 id=&quot;talking-machines&quot;&gt;Talking Machines&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;https://www.thetalkingmachines.com/&quot;&gt;https://www.thetalkingmachines.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/tlkngmchns?lang=en&quot;&gt;@TlkngMchns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;http://rss.art19.com/talking-machines&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/talking-machines/id955198749?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/talking-machines-1457195&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid border w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-talking-machines.c0e6115bbb37.jpg&quot; width=&quot;500&quot; height=&quot;500&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-talking-machines.c0e6115bbb37.jpg&amp;amp;w=125&amp;amp;sig=401e1db9f2da5013bf74d839e4c276d13bc81e20 125w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-talking-machines.c0e6115bbb37.jpg&amp;amp;w=250&amp;amp;sig=bb504065cadbdcef25940407d85ba41506e8c327 250w, https://files.realpython.com/media/podcast-logo-talking-machines.c0e6115bbb37.jpg 500w&quot; sizes=&quot;75vw&quot; alt=&quot;Talking Machines Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Former public radio producer Katherine Gorman believes that continuing the public conversation about data science, AI, and machine learning is absolutely essential to preventing another AI winter.&lt;/p&gt;
&lt;p&gt;She believes that data science podcasts are a great venue for that discussion. To this end, she hosts Talking Machines along with Professor Neil Lawrence.&lt;/p&gt;
&lt;p&gt;The podcast aims to introduce machine learning to a wide audience and help industry professionals, business leaders, and interested laypeople better understand these tools and technologies.&lt;/p&gt;
&lt;p&gt;The episodes generally follow a simple format: the hosts chat about industry news, interview a guest, and in the end may answer a listener question. Episodes are released in seasons and tend to be on the longer side at around 40 minutes.&lt;/p&gt;
&lt;p&gt;This is where Katherine&amp;rsquo;s history as a radio host comes in handy: she keeps the show engaging and informative, and works hard to make sure the it presents an accurate picture of the machine learning industry.&lt;/p&gt;
&lt;h3 id=&quot;oreilly-data-show&quot;&gt;O&amp;rsquo;Reilly Data Show&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;https://www.oreilly.com/topics/oreilly-data-show-podcast&quot;&gt;https://www.oreilly.com/topics/oreilly-data-show-podcast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/oreillymedia?lang=en&quot;&gt;@OReillyMedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;http://feeds.podtrac.com/IOJSwQcdEBcg&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/oreilly-data-show-oreilly-media-podcast/id944929220?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/is26f-4d5a0/The-O&#39;Reilly-Data-Show-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/oreilly-data-show-oreilly-media-podcast&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-oreilly-data.a0d9828f8f32.jpg&quot; width=&quot;630&quot; height=&quot;630&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-oreilly-data.a0d9828f8f32.jpg&amp;amp;w=157&amp;amp;sig=a9fef5e9e0c51ec0c2d791728e1d972f7acef0a9 157w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-oreilly-data.a0d9828f8f32.jpg&amp;amp;w=315&amp;amp;sig=2ff5806fad02ace4c0046e43cd45e7d021840eda 315w, https://files.realpython.com/media/podcast-logo-oreilly-data.a0d9828f8f32.jpg 630w&quot; sizes=&quot;75vw&quot; alt=&quot;O&amp;#39;Reilly Data Show Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Ben Lorica is the Chief Data Scientist at O&amp;rsquo;Reilly Media. In each episode, he is joined by an industry professional to discuss topics in big data and data science. The episodes run anywhere from 30 to 40 minutes and are very accessible to listen to.&lt;/p&gt;
&lt;p&gt;At the beginning of each episode, the host promotes an event series that listeners can attend to learn more about the topics covered in the podcast. The ones mentioned in the intro are the Strata Data Conference and the Artificial Intelligence Conference, but you can find more of the O&amp;rsquo;Reilly conferences on &lt;a href=&quot;https://www.oreilly.com/conferences/&quot;&gt;their event page&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;not-so-standard-deviations&quot;&gt;Not So Standard Deviations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;http://nssdeviations.com/&quot;&gt;http://nssdeviations.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/nssdeviations?lang=en&quot;&gt;@NSSDeviations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;http://nssdeviations.libsyn.com/rss&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/not-so-standard-deviations/id1040614570?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/cjmtq-387c6/Not-So-Standard-Deviations-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/1504035&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid border w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-standard-deviations.73de29576353.png&quot; width=&quot;512&quot; height=&quot;512&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-standard-deviations.73de29576353.png&amp;amp;w=128&amp;amp;sig=e3cf45b33416135bab97398b1869450293c93e87 128w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-standard-deviations.73de29576353.png&amp;amp;w=256&amp;amp;sig=b5d3baa8b7a4583c86a5800081fc743d8af0f79b 256w, https://files.realpython.com/media/podcast-logo-standard-deviations.73de29576353.png 512w&quot; sizes=&quot;75vw&quot; alt=&quot;Not So Standard Deviations Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Roger Peng (of the Johns Hopkins Bloomberg School of Public Health) and Hilary Parker (of Stitch Fix) co-host this podcast. They discuss industry news as well as their personal experiences working with data.&lt;/p&gt;
&lt;p&gt;Episodes air two or three times a month and can run on the longer side. Most episodes are at least 60 minutes, with some clocking in at almost an hour and a half. These are great for when you have a long commute or spend an evening at home doing chores, so you can really get into the discussion!&lt;/p&gt;
&lt;h3 id=&quot;data-stories&quot;&gt;Data Stories&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;http://datastori.es/&quot;&gt;http://datastori.es/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/datastories?lang=en&quot;&gt;@datastories&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;http://datastori.es/feed/mp3/&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/data-stories/id502854960?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/data-stories/ep-133-year-review-2018&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-data-stories.85d966ff2016.png&quot; width=&quot;400&quot; height=&quot;400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-data-stories.85d966ff2016.png&amp;amp;w=100&amp;amp;sig=2c16daeabc8c6cb839e2ebef582938378da443e1 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-data-stories.85d966ff2016.png&amp;amp;w=200&amp;amp;sig=278d48880bad85535a661766dfee019311879ff4 200w, https://files.realpython.com/media/podcast-logo-data-stories.85d966ff2016.png 400w&quot; sizes=&quot;75vw&quot; alt=&quot;Data Stories Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This podcast on &lt;a href=&quot;https://realpython.com/python-data-visualization-bokeh/&quot;&gt;data visualization&lt;/a&gt; focuses on a very specific subset of the data analysis pipeline—a rare gem among data science podcasts. Data viz specialists Enrico Bertini and Moritz Stefaner sit down with a guest every other week to discuss data analysis and visualization.&lt;/p&gt;
&lt;p&gt;The show has quite a conversational tone. The hosts bounce ideas off of one another, ask great questions of their guests, and generally keep the conversation flowing. With around 40 minutes of runtime, listeners can settle in to really learn about how we can better visualize our data, as well as the role that data plays in our everyday lives.&lt;/p&gt;
&lt;h3 id=&quot;superdatascience&quot;&gt;SuperDataScience&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;https://www.superdatascience.com/podcast/&quot;&gt;https://www.superdatascience.com/podcast/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/superdatasci?lang=en&quot;&gt;@superdatasci&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;https://feeds.soundcloud.com/users/soundcloud:users:253585900/sounds.rss&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/superdatascience/id1163599059?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/if3pi-46ad1/SuperDataScience-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/superdatascience&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-superdatascience.bf6e8a48a717.jpg&quot; width=&quot;500&quot; height=&quot;500&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-superdatascience.bf6e8a48a717.jpg&amp;amp;w=125&amp;amp;sig=a0eb3ca39746116335a336434425c9dcda7950f8 125w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-superdatascience.bf6e8a48a717.jpg&amp;amp;w=250&amp;amp;sig=c83cdb5aebe207f3911d26d04f0f7601b3315cc4 250w, https://files.realpython.com/media/podcast-logo-superdatascience.bf6e8a48a717.jpg 500w&quot; sizes=&quot;75vw&quot; alt=&quot;SuperDataScience Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Kirill Eremenko is a data science coach and lifestyle entrepreneur, and he brings his experience as an influencer to the SuperDataScience podcast. In his interview episodes, he talks with data scientists and data analysts to learn more about their career paths and how they were able to succeed in the data industry.&lt;/p&gt;
&lt;p&gt;In addition to interviewing industry experts, the host airs minisodes that are purely inspirational! Called &lt;em&gt;Five Minute Friday&lt;/em&gt;, these minisodes aim to inspire listeners to improve themselves as data scientists, and to offer advice on how to advance in a data science career. This is definitely one of the most motivational data science podcasts out there!&lt;/p&gt;
&lt;h3 id=&quot;data-science-at-home&quot;&gt;Data Science at Home&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;https://datascienceathome.com/&quot;&gt;https://datascienceathome.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/thisisfrag?lang=en&quot;&gt;@ThisIsFrag&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;https://podcast.datascienceathome.com/feed.xml&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/data-science-at-home/id1069871378?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/5hra9-70b54/Data-Science-at-Home-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/data-science-at-home-2362678&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-ds-at-home.b2e1888b570f.png&quot; width=&quot;1400&quot; height=&quot;1400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-ds-at-home.b2e1888b570f.png&amp;amp;w=350&amp;amp;sig=d3f94aafb21b9361939b66a181033a5c785f6526 350w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-ds-at-home.b2e1888b570f.png&amp;amp;w=700&amp;amp;sig=db041a40905f7a3ab12ddc203cdfeaa4647f1238 700w, https://files.realpython.com/media/podcast-logo-ds-at-home.b2e1888b570f.png 1400w&quot; sizes=&quot;75vw&quot; alt=&quot;Data Science At Home Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Francesco Gadaleta wants to make machine learning easy for everyone. In this podcast, he alternates between interview episodes with industry experts, and solo episodes where he discusses a topic on his own.&lt;/p&gt;
&lt;p&gt;The show doesn&amp;rsquo;t seem to be on a fixed schedule, and the episode length varies as well, but in general the interview episodes run closer to an hour, while his solo episodes clock in at around twenty minutes.&lt;/p&gt;
&lt;p&gt;The host is pretty opinionated, so it can be interesting to hear his perspective on topics like AI winter, optimization, and the minimum requirements you need to become a data scientist.&lt;/p&gt;
&lt;h3 id=&quot;this-week-in-machine-learning-artificial-intelligence-twimlai&quot;&gt;This Week in Machine Learning &amp;amp; Artificial Intelligence (TWiML&amp;amp;AI)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;https://twimlai.com/&quot;&gt;https://twimlai.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/twimlai?lang=en&quot;&gt;@twimlai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;http://feeds.feedburner.com/twimlai&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/this-week-in-machine-learning/id1116303051?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/5jknv-4146f/This-Week-in-Machine-Learning--Artificial-Intelligence-(AI)-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/2355587&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid border w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-twimlai.b5d07d070083.jpg&quot; width=&quot;500&quot; height=&quot;500&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-twimlai.b5d07d070083.jpg&amp;amp;w=125&amp;amp;sig=c86a2a9b08aa6069a576366136ca14de86efd9d3 125w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-twimlai.b5d07d070083.jpg&amp;amp;w=250&amp;amp;sig=5e93bd2519688ea744a172ac7b852ae9023d525b 250w, https://files.realpython.com/media/podcast-logo-twimlai.b5d07d070083.jpg 500w&quot; sizes=&quot;75vw&quot; alt=&quot;This Week In Machine Learning And AI TWIMLAI Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;TWiML&amp;amp;AI is a weekly podcast that discusses the latest developments in data science, machine learning, and artificial intelligence. The host Sam Charrington interviews leading researchers and industry experts to inform a growing community of academics, engineers, business leaders, and other machine learning and AI enthusiasts.&lt;/p&gt;
&lt;p&gt;The show caters to a highly targeted audience, and can be pretty technical at times. Listeners who are not industry professionals may need to brush up on background knowledge in order to get the most out of each episode.&lt;/p&gt;
&lt;p&gt;There are over two hundred hour-long episodes to listen to. Because the podcast discusses recent developments in this tech space, you can jump right into the latest episode, or head back in the archives and check in on some historical developments in machine learning and AI.&lt;/p&gt;
&lt;h3 id=&quot;dataframed&quot;&gt;DataFramed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;https://www.datacamp.com/community/podcast&quot;&gt;https://www.datacamp.com/community/podcast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/DataCamp?lang=en&quot;&gt;@DataCamp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;https://itunes.apple.com/us/podcast/dataframed/id1336150688?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/djrvq-63d37/DataFramed-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/series-2285898&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-dataframed.7872c445cee4.jpg&quot; width=&quot;500&quot; height=&quot;500&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-dataframed.7872c445cee4.jpg&amp;amp;w=125&amp;amp;sig=1b7ff631e5d75991d769f643872c53eb7ae515e2 125w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-dataframed.7872c445cee4.jpg&amp;amp;w=250&amp;amp;sig=6f8cf63c7038e749e3712413e3288f3a220f951f 250w, https://files.realpython.com/media/podcast-logo-dataframed.7872c445cee4.jpg 500w&quot; sizes=&quot;75vw&quot; alt=&quot;DataFramed Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Data scientist, writer, and educator Hugo Bowne-Anderson hosts this podcast sponsored by DataCamp.&lt;/p&gt;
&lt;p&gt;Each week, the host sits down with industry professionals and academic experts to discuss how the data science industry is impacting the world. The host asks great questions and invites guests who discuss interesting developments in the field as well as their own personal projects.&lt;/p&gt;
&lt;p&gt;DataFramed also has short segments spaced throughout the episodes that give the listener more information on certain topics. For instance, in &lt;em&gt;Freelance Data Science&lt;/em&gt;, Hugo and Susan Sun talk about how to navigate the data science space as an independent contractor. Justin Boyce gives practical advice on improving workflow in &lt;em&gt;Data Science Best Practices&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Because it is sponsored by DataCamp, their products are pitched a lot, so it can feel a bit sales-y at times. Still, the show is interesting and informative, and Hugo does a great job of drawing in the listener.&lt;/p&gt;
&lt;h3 id=&quot;learning-machines-101&quot;&gt;Learning Machines 101&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;https://www.learningmachines101.com/&quot;&gt;https://www.learningmachines101.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/lm101talk&quot;&gt;@lm101talk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;http://learningmachines101.libsyn.com/rss&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/learning-machines-101/id892779679?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/8hbz5-34db0/Learning-Machines-101-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/learning-machines-101&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid border w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-learning-machines.3400cf56a8b0.jpg&quot; width=&quot;1400&quot; height=&quot;1400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-learning-machines.3400cf56a8b0.jpg&amp;amp;w=350&amp;amp;sig=297540b014626015fe771194b0d6b108c3f8da3a 350w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-learning-machines.3400cf56a8b0.jpg&amp;amp;w=700&amp;amp;sig=3c07ed0687b15431576ff07515fe52f7b39f3f55 700w, https://files.realpython.com/media/podcast-logo-learning-machines.3400cf56a8b0.jpg 1400w&quot; sizes=&quot;75vw&quot; alt=&quot;Learning Machines 101 Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Dr. Richard Golden, Professor of Cognitive Science and Electrical Engineering, hosts Learning Machines 101. The podcast aims to explain advanced concepts in machine learning and artificial intelligence to a wide audience.&lt;/p&gt;
&lt;p&gt;Still, the episodes can get pretty technical, covering topics such as knowledge representation, expectation maximization, and spectral clustering.&lt;/p&gt;
&lt;p&gt;Listeners might need to listen more than once to really grasp the topic at hand. This shouldn&amp;rsquo;t be too hard, as the episodes are no more than half an hour long and aren&amp;rsquo;t released too often. (Only 74 episodes have been released since April 2014.)&lt;/p&gt;
&lt;p&gt;Listeners can use this podcast as a jumping-off point into more advanced machine learning topics.&lt;/p&gt;
&lt;h3 id=&quot;artificial-intelligence-in-industry&quot;&gt;Artificial Intelligence in Industry&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;http://techemergence.libsyn.com/&quot;&gt;http://techemergence.libsyn.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/emerj&quot;&gt;@Emerj&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Subscribe: &lt;a href=&quot;http://techemergence.libsyn.com/rss&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/artificial-intelligence-in-industry-with-dan-faggella/id670771965?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/ix3i9-41718/Artificial-Intelligence-in-Industry-with-Dan-Faggella-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/artificial-intelligence-in-industry-with-dan-faggella&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid border w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-aiii.26b15dffa3df.jpg&quot; width=&quot;630&quot; height=&quot;630&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-aiii.26b15dffa3df.jpg&amp;amp;w=157&amp;amp;sig=a970c0f25073c2141ccd78b95f64f62fd320476a 157w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-aiii.26b15dffa3df.jpg&amp;amp;w=315&amp;amp;sig=382dbf90870942379fce3e21dde938d1695d8364 315w, https://files.realpython.com/media/podcast-logo-aiii.26b15dffa3df.jpg 630w&quot; sizes=&quot;75vw&quot; alt=&quot;Artificial Intelligence In Industry Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This weekly podcast is focused on the practical applications of artificial intelligence in business settings. The episodes are short, insightful, and easy to understand. Within half an hour, host Dan Faggella interviews AI professionals to see how the technology is used in industries from finance and government to retail and education.&lt;/p&gt;
&lt;p&gt;Together, Dan and his guests answer questions like &amp;ldquo;How can you use AI to hire employees?&amp;rdquo; and &amp;ldquo;When should you upgrade your AI hardware?&amp;rdquo; They touch on each topic just long enough to pique the listener&amp;rsquo;s interest and encourage them to dive deeper on their own later.&lt;/p&gt;
&lt;h2 id=&quot;archived-data-science-podcasts&quot;&gt;Archived Data Science Podcasts&lt;/h2&gt;
&lt;p&gt;As of this writing, these data science podcasts have run their course. The archives are still available for you to dive into, and are chock-full of useful information, so don&amp;rsquo;t hesitate to dive right in!&lt;/p&gt;
&lt;h3 id=&quot;partially-derivative&quot;&gt;Partially Derivative&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;http://partiallyderivative.com/&quot;&gt;http://partiallyderivative.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/partiallyd?lang=en&quot;&gt;@partiallyd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;https://itunes.apple.com/us/podcast/partially-derivative/id942048597?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/a3zq8-34db2/Partially-Derivative-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/partially-derivative&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-partially-derivative.303d4ea241b7.png&quot; width=&quot;512&quot; height=&quot;512&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-partially-derivative.303d4ea241b7.png&amp;amp;w=128&amp;amp;sig=bd6e09631d94bdfdea53e3e14d5624c0d279dda7 128w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-partially-derivative.303d4ea241b7.png&amp;amp;w=256&amp;amp;sig=a9c93ef25f10910965b49d634d1d4b5f77453d35 256w, https://files.realpython.com/media/podcast-logo-partially-derivative.303d4ea241b7.png 512w&quot; sizes=&quot;75vw&quot; alt=&quot;Partially Derivative Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;If you like heading to the bar and chatting about industry news with your fellow data scientists, then this is one of the best data science podcasts for you! Jonathan Morgan, Vidya Spandana, and Chris Albon get together to down a few drinks and discuss the latest in data science.&lt;/p&gt;
&lt;p&gt;The episodes can run anywhere from 20 minutes to an hour, but generally clock in at around 30 to 40 minutes. While the show is no longer running, there are over one hundred episodes in the archive.&lt;/p&gt;
&lt;p&gt;Listeners can delve into the backlog and learn about data scraping, bias models, and pair-programming in Python, as well as review some of the trending news stories of years past.&lt;/p&gt;
&lt;h3 id=&quot;machine-learning-guide-machine-learning-applied&quot;&gt;Machine Learning Guide / Machine Learning Applied&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;http://ocdevel.com/mlg&quot;&gt;http://ocdevel.com/mlg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/lefnire&quot;&gt;@lefnire&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;http://machinelearningguide.libsyn.com/rss&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/machine-learning-guide/id1204521130&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/se4er-4c31d/Machine-Learning-Guide-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/machine-learning-guide-1457335&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid border w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-ml-guide.d6a2aba891f9.jpg&quot; width=&quot;1400&quot; height=&quot;1400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-ml-guide.d6a2aba891f9.jpg&amp;amp;w=350&amp;amp;sig=93ee4323026b5ef7f0be3cfc60ec8753892adcfb 350w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-ml-guide.d6a2aba891f9.jpg&amp;amp;w=700&amp;amp;sig=89372fe2dcb4ad65b9c7b95f44c93e31f086428d 700w, https://files.realpython.com/media/podcast-logo-ml-guide.d6a2aba891f9.jpg 1400w&quot; sizes=&quot;75vw&quot; alt=&quot;Machine Learning Guide Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;These data science podcasts are both run by Tyler Renelli, and each has a slightly different approach to machine learning and AI.&lt;/p&gt;
&lt;p&gt;Machine Learning Guide (MLG) aims to gently introduce listeners to the world of machine learning by explaining topics from the ground up, from the classic algorithms (linear and logistic regression) on up to reinforcement learning and hyperparameters.&lt;/p&gt;
&lt;p&gt;The episodes run anywhere from 45 minutes to an hour, but it&amp;rsquo;s easy to become engrossed in Tyler&amp;rsquo;s explanations. It&amp;rsquo;s the perfect podcast to complement other activities, like commuting, exercising, or cleaning up around the house.&lt;/p&gt;
&lt;p&gt;One of the best parts of this podcast is the curated learning resources that the host provides at the end of each episode. After listening to a high-lever overview, you can dive deeper into the topic by taking a recommended course or reading a suggested textbook.&lt;/p&gt;
&lt;p&gt;His episode on languages and frameworks includes a link to a primer on Python deep learning frameworks. If you follow the episodes in order from beginning to end, and complete the supplemental resources, you will have quite a detailed foundation in machine learning.&lt;/p&gt;
&lt;p&gt;As of this writing, MLG has run its course at 29 full-length episodes.&lt;/p&gt;
&lt;p&gt;A second podcast called Machine Learning Applied is currently airing, where Tyler focuses on the more practical aspects of machine learning. He answers questions such as what kind of salary one can expect, the best way to store data, and how to get the most out of Jupyter notebooks. Listeners can gain access to Machine Learning Applied by &lt;a href=&quot;https://www.patreon.com/machinelearningguide&quot;&gt;becoming a supporter on Patroen&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;becoming-a-data-scientist&quot;&gt;Becoming a Data Scientist&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Website: &lt;a href=&quot;https://www.becomingadatascientist.com/category/podcast/&quot;&gt;https://www.becomingadatascientist.com/category/podcast/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href=&quot;https://twitter.com/becomingdatasci&quot;&gt;@BecomingDataSci&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Listen: &lt;a href=&quot;https://www.becomingadatascientist.com/feed/podcast&quot;&gt;RSS&lt;/a&gt; ⋅ &lt;a href=&quot;https://itunes.apple.com/us/podcast/becoming-a-data-scientist-podcast/id1076448558?mt=2&quot;&gt;iTunes&lt;/a&gt; ⋅ &lt;a href=&quot;https://www.podbean.com/podcast-detail/bxadb-3e2bc/Becoming-A-Data-Scientist-Podcast&quot;&gt;Podbean&lt;/a&gt; ⋅ &lt;a href=&quot;https://player.fm/series/becoming-a-data-scientist-podcast&quot;&gt;Player FM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid border w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/podcast-logo-becoming-datasci.db4c6a408e27.jpg&quot; width=&quot;630&quot; height=&quot;630&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-becoming-datasci.db4c6a408e27.jpg&amp;amp;w=157&amp;amp;sig=38925176abd1ed6c333514b99044f126aa4cb4a2 157w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/podcast-logo-becoming-datasci.db4c6a408e27.jpg&amp;amp;w=315&amp;amp;sig=5bc5a31f74af6be1cc54485500eb1780211b77ab 315w, https://files.realpython.com/media/podcast-logo-becoming-datasci.db4c6a408e27.jpg 630w&quot; sizes=&quot;75vw&quot; alt=&quot;Becoming A Data Scientist Podcast Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This podcast does exactly what its title says. The host, Renee Teate, sits down each week with someone who is on their way to &amp;ldquo;becoming a data scientist.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;She interviews other data science professionals to see exactly how they were able to carve a path for themselves into the industry. In the very first episode, Renee talks about her own journey into becoming a data scientist as she transitions from her previous role as a data analyst.&lt;/p&gt;
&lt;p&gt;As of this writing, the podcast is not currently active. The last episodes came out in early 2017. Still, there is a wealth of information contained in the twenty hour-long episodes that have aired.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re just beginning your foray into the data science world, take a weekend to blast through the archive and see where the possibilities lie!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This list is not exhaustive! There are new podcasts airing all the time, and we can only expect the number of data science podcasts to grow as the field continues to explode in popularity.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t see your favorite show on this list? Leave us a comment down below and let us know your favorite data science podcasts!&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 Development in Visual Studio Code</title>
      <id>https://realpython.com/python-development-visual-studio-code/</id>
      <link href="https://realpython.com/python-development-visual-studio-code/"/>
      <updated>2019-02-04T06:00:00+00:00</updated>
      <summary>In this tutorial, you&#39;ll learn how to use Visual Studio Code for Python development. By following examples, you&#39;ll cover everything from how to install and configure Visual Studio Code for Python development to how to run tests and debug application, so you can use this powerful tool.</summary>
      <content type="html">
        &lt;p&gt;One of the coolest code editors available to programmers, &lt;a href=&quot;https://code.visualstudio.com/docs&quot;&gt;Visual Studio Code&lt;/a&gt;, is an open-source, extensible, light-weight editor available on all platforms. It&amp;rsquo;s these qualities that make Visual Studio Code from &lt;a href=&quot;https://azure.com/python&quot;&gt;Microsoft&lt;/a&gt; very popular, and a great platform for Python development.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn about Python development in Visual Studio Code, including how to:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install Visual Studio Code&lt;/li&gt;
&lt;li&gt;Discover and install extensions that make Python development easy&lt;/li&gt;
&lt;li&gt;Write a simple Python application&lt;/li&gt;
&lt;li&gt;Learn how to run and debug existing Python programs in VS Code&lt;/li&gt;
&lt;li&gt;Connect Visual Studio Code to Git and GitHub to share your code with the world&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We assume you are familiar with Python development and already have some form of Python installed on your system (Python 2.7, Python 3.6/3.7, Anaconda, or others). Screenshots and demos for Ubuntu and Windows are provided. Because Visual Studio Code runs on all major platforms, you may see slightly different UI elements and may need to modify certain commands.&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;installing-and-configuring-visual-studio-code-for-python-development&quot;&gt;Installing and Configuring Visual Studio Code for Python Development&lt;/h2&gt;
&lt;p&gt;Installing Visual Studio Code is &lt;a href=&quot;https://code.visualstudio.com/docs/setup/setup-overview&quot;&gt;very accessible&lt;/a&gt; on any platform. Full instructions for &lt;a href=&quot;https://code.visualstudio.com/docs/setup/windows&quot;&gt;Windows&lt;/a&gt;, &lt;a href=&quot;https://code.visualstudio.com/docs/setup/mac&quot;&gt;Mac&lt;/a&gt;, and &lt;a href=&quot;https://code.visualstudio.com/docs/setup/linux&quot;&gt;Linux&lt;/a&gt; are available, and the editor is updated monthly with new features and bug fixes. You can find everything at the &lt;a href=&quot;https://code.visualstudio.com&quot;&gt;Visual Studio Code website&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-website.491c40d9d828.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/vscode-website.491c40d9d828.png&quot; width=&quot;1198&quot; height=&quot;832&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-website.491c40d9d828.png&amp;amp;w=299&amp;amp;sig=29968507a8ad40de80cac07ba0088fb27db4224a 299w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-website.491c40d9d828.png&amp;amp;w=599&amp;amp;sig=e26716f2b3924418cefaed81f407b7d448fcb6e2 599w, https://files.realpython.com/media/vscode-website.491c40d9d828.png 1198w&quot; sizes=&quot;75vw&quot; alt=&quot;Visual Studio Code Web Site&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In case you were wondering, Visual Studio Code (or VS Code for short) shares almost nothing other than a name with its larger Windows-based namesake, &lt;a href=&quot;https://visualstudio.microsoft.com/vs/features/python/&quot;&gt;Visual Studio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Visual Studio Code has built-in support for multiple languages and an extension model with a rich ecosystem of support for others. VS Code is updated monthly, and you can keep up to date at the &lt;a href=&quot;http://aka.ms/pythonblog&quot;&gt;Microsoft Python blog&lt;/a&gt;. Microsoft even makes the &lt;a href=&quot;https://github.com/Microsoft/vscode&quot;&gt;VS Code GitHub repo&lt;/a&gt; available for anyone to clone and contribute. (Cue the PR flood.)&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://code.visualstudio.com/docs/getstarted/userinterface&quot;&gt;VS Code UI&lt;/a&gt; is well documented, so I won&amp;rsquo;t rehash it here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-welcome-screen.c64afd719b3e.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/vscode-welcome-screen.c64afd719b3e.png&quot; width=&quot;1204&quot; height=&quot;820&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-welcome-screen.c64afd719b3e.png&amp;amp;w=301&amp;amp;sig=3df2b95d31ddbbd355e94eeb35fd5ad3a624c421 301w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-welcome-screen.c64afd719b3e.png&amp;amp;w=602&amp;amp;sig=d9223d409ea7e06bd146d16da3aac2b1cdf22d42 602w, https://files.realpython.com/media/vscode-welcome-screen.c64afd719b3e.png 1204w&quot; sizes=&quot;75vw&quot; alt=&quot;Visual Studio Code Welcome Screen&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;extensions-for-python-development&quot;&gt;Extensions for Python Development&lt;/h3&gt;
&lt;p&gt;As stated above, VS Code supports development in multiple programming languages through a well-documented &lt;a href=&quot;https://code.visualstudio.com/docs/editor/extension-gallery&quot;&gt;extension model&lt;/a&gt;. The &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-python.python&quot;&gt;Python extension&lt;/a&gt; enables Python development in Visual Studio Code, with the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support for Python 3.4 and higher, as well as Python 2.7&lt;/li&gt;
&lt;li&gt;Code completion with &lt;a href=&quot;https://code.visualstudio.com/docs/editor/intellisense&quot;&gt;IntelliSense&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/python/linting&quot;&gt;Linting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/python/debugging&quot;&gt;Debugging support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/editor/userdefinedsnippets&quot;&gt;Code snippets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/python/unit-testing&quot;&gt;Unit testing support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Automatic use of &lt;a href=&quot;https://code.visualstudio.com/docs/python/environments&quot;&gt;conda and virtual environments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Code editing in &lt;a href=&quot;https://code.visualstudio.com/docs/python/editing#_jupyter-code-cells&quot;&gt;Jupyter environments&lt;/a&gt; and &lt;a href=&quot;https://code.visualstudio.com/docs/python/editing#_open-jupyter-notebooks&quot;&gt;Jupyter Notebooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/python-extension-webpage.d2a7d3b6d636.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/python-extension-webpage.d2a7d3b6d636.png&quot; width=&quot;1198&quot; height=&quot;832&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/python-extension-webpage.d2a7d3b6d636.png&amp;amp;w=299&amp;amp;sig=94245e20c2c9dd8d495da91c5740c3e3c5ad4592 299w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/python-extension-webpage.d2a7d3b6d636.png&amp;amp;w=599&amp;amp;sig=093ab4791bda5d946f0c78ab5b3572cdfb875eff 599w, https://files.realpython.com/media/python-extension-webpage.d2a7d3b6d636.png 1198w&quot; sizes=&quot;75vw&quot; alt=&quot;Installing the Python extension for VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Visual Studio Code extensions cover more than just programming language capabilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/search?target=VSCode&amp;amp;category=Keymaps&amp;amp;sortBy=Downloads&quot;&gt;Keymaps&lt;/a&gt; allow users already familiar with Atom, &lt;a href=&quot;https://realpython.com/setting-up-sublime-text-3-for-full-stack-python-development/&quot;&gt;Sublime Text&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/emacs-the-best-python-editor/&quot;&gt;Emacs&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/vim-and-python-a-match-made-in-heaven/&quot;&gt;Vim&lt;/a&gt;, PyCharm, or other environments to feel at home.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/search?target=VSCode&amp;amp;category=Themes&amp;amp;sortBy=Downloads&quot;&gt;Themes&lt;/a&gt; customize the UI whether you like coding in the light, dark, or something more colorful.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/search?target=VSCode&amp;amp;category=Language%20Packs&amp;amp;sortBy=Downloads&quot;&gt;Language packs&lt;/a&gt; provide a localized experience.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are some other extensions and settings I find useful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens&quot;&gt;GitLens&lt;/a&gt; provides tons of useful Git features directly in your editing window, including blame annotations and repository exploration features.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Auto save is easily turned on by selecting &lt;code&gt;File, Auto Save&lt;/code&gt; from the menu. The default delay time is 1000 milliseconds, which is also &lt;a href=&quot;https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save&quot;&gt;configurable&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync&quot;&gt;Settings Sync&lt;/a&gt; allows you to synchronize your VS Code settings across different installations using GitHub. If you work on different machines, this helps keep your environment consistent across them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/azure/docker&quot;&gt;Docker&lt;/a&gt; lets you quickly and easily work with Docker, helping author &lt;code&gt;Dockerfile&lt;/code&gt; and &lt;code&gt;docker-compose.yml&lt;/code&gt;, package and deploy your projects, and even generate the proper Docker files for your project.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, you may discover other useful extensions as you use VS Code. Please share your discoveries and settings in the &lt;a href=&quot;https://realpython.com/python-development-with-visual-studio-code/#reader-comments&quot;&gt;comments&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Discovering and installing new extensions and themes is accessible by clicking on the &lt;em&gt;Extensions&lt;/em&gt; icon on the Activity Bar. You can search for extensions using keywords, sort the results numerous ways, and install extensions quickly and easily. For this article, install the Python extension by typing &lt;code&gt;python&lt;/code&gt; in the &lt;em&gt;Extensions&lt;/em&gt; item on the Activity Bar, and clicking &lt;em&gt;Install&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-marketplace.25e99aec9f68.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/vscode-marketplace.25e99aec9f68.gif&quot; width=&quot;1189&quot; height=&quot;915&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-marketplace.25e99aec9f68.gif&amp;amp;w=297&amp;amp;sig=2eddde1ed3ac660512e9b44145ed2c21766ea32a 297w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-marketplace.25e99aec9f68.gif&amp;amp;w=594&amp;amp;sig=1f6b503b0aa6038b227442bab417356031fad8c9 594w, https://files.realpython.com/media/vscode-marketplace.25e99aec9f68.gif 1189w&quot; sizes=&quot;75vw&quot; alt=&quot;Finding the VSCode Marketplace in the UI&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can find and install any of the extensions mentioned above in the same manner.&lt;/p&gt;
&lt;h3 id=&quot;visual-studio-code-configuration-files&quot;&gt;Visual Studio Code Configuration Files&lt;/h3&gt;
&lt;p&gt;One important thing to mention is that Visual Studio Code is highly configurable through &lt;a href=&quot;https://code.visualstudio.com/docs/getstarted/settings&quot;&gt;user and workspace settings&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;User settings are global across all Visual Studio Code instances, while workspace settings are local to the specific folder or project workspace. Workspace settings give VS Code tons of flexibility, and I call out workspace settings throughout this article. Workspace settings are stored as &lt;code&gt;.json&lt;/code&gt; files in a folder local to the project workspace called &lt;code&gt;.vscode&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;start-a-new-python-program&quot;&gt;Start a New Python Program&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start our exploration of Python development in Visual Studio Code with a new Python program. In VS Code, type &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-n&quot;&gt;N&lt;/kbd&gt;&lt;/span&gt; to open a new File. (You can also select &lt;em&gt;File, New&lt;/em&gt; from the menu.)&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 Visual Studio Code UI provides the &lt;a href=&quot;https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette&quot;&gt;Command Palette&lt;/a&gt;, from which you can search and execute any command without leaving the keyboard. Open the Command Palette using &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-control&quot;&gt;Ctrl&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-shift&quot;&gt;Shift&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-p&quot;&gt;P&lt;/kbd&gt;&lt;/span&gt;, type &lt;code&gt;File: New File&lt;/code&gt;, and hit &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-enter&quot;&gt;Enter&lt;/kbd&gt;&lt;/span&gt; to open a new file.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;No matter how you get there, you should see a VS Code window that looks similar to the following:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-new-file.39cc7b9e485d.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/vscode-new-file.39cc7b9e485d.png&quot; width=&quot;1204&quot; height=&quot;820&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-new-file.39cc7b9e485d.png&amp;amp;w=301&amp;amp;sig=dc1cf2fab3d75f8a29b54d9758e0180476a3d9f1 301w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-new-file.39cc7b9e485d.png&amp;amp;w=602&amp;amp;sig=437dd35b3b89c2939475117373bf800b7826cd27 602w, https://files.realpython.com/media/vscode-new-file.39cc7b9e485d.png 1204w&quot; sizes=&quot;75vw&quot; alt=&quot;Creating a new file in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once a new file is opened, you can being entering code.&lt;/p&gt;
&lt;h3 id=&quot;entering-python-code&quot;&gt;Entering Python Code&lt;/h3&gt;
&lt;p&gt;For our test code, let&amp;rsquo;s quickly code up the &lt;a href=&quot;https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes&quot;&gt;Sieve of Eratosthenes&lt;/a&gt; (which finds all primes less than a given number). Begin typing the following code in the new tab you just opened:&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;sieve&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&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;2&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;You should see something similar to this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-unformatted-code.e11b194803fe.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/vscode-unformatted-code.e11b194803fe.png&quot; width=&quot;1204&quot; height=&quot;820&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-unformatted-code.e11b194803fe.png&amp;amp;w=301&amp;amp;sig=07f637c70465bc5e893d363c898c40bb3741a3f4 301w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-unformatted-code.e11b194803fe.png&amp;amp;w=602&amp;amp;sig=0649eb650cf53783005acf4d43e10b3229642269 602w, https://files.realpython.com/media/vscode-unformatted-code.e11b194803fe.png 1204w&quot; sizes=&quot;75vw&quot; alt=&quot;Unformatted code in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Wait, what&amp;rsquo;s going on? Why isn&amp;rsquo;t Visual Studio Code doing any keyword highlighting, any auto-formatting, or anything really helpful? What gives?&lt;/p&gt;
&lt;p&gt;The answer is that, right now, VS Code doesn&amp;rsquo;t know what kind of file it&amp;rsquo;s dealing with. The buffer is called &lt;code&gt;Untitled-1&lt;/code&gt;, and if you look in the lower right corner of the window, you&amp;rsquo;ll see the words &lt;em&gt;Plain Text&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To activate the Python extension, save the file (by selecting &lt;em&gt;File, Save&lt;/em&gt; from the menu, &lt;em&gt;File:Save File&lt;/em&gt; from the Command Palette, or just using &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-s&quot;&gt;S&lt;/kbd&gt;&lt;/span&gt;) as &lt;code&gt;sieve.py&lt;/code&gt;. VS Code will see the &lt;code&gt;.py&lt;/code&gt; extension and correctly interpret the file as Python code. Now your window should look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-formatted-code.312b8d79fbe7.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/vscode-formatted-code.312b8d79fbe7.png&quot; width=&quot;1204&quot; height=&quot;820&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-formatted-code.312b8d79fbe7.png&amp;amp;w=301&amp;amp;sig=07e7b9736d47dc109e42374b901add6a2db41cf7 301w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-formatted-code.312b8d79fbe7.png&amp;amp;w=602&amp;amp;sig=20181f702d4cb6da41b2a2d6af7a87d69f84f42c 602w, https://files.realpython.com/media/vscode-formatted-code.312b8d79fbe7.png 1204w&quot; sizes=&quot;75vw&quot; alt=&quot;Properly formatted Python code in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s much better! VS Code automatically reformats the file as Python, which you can verify by inspecting the language mode in the lower left corner.&lt;/p&gt;
&lt;p&gt;If you have multiple Python installations (like Python 2.7, Python 3.x, or Anaconda), you can change which Python interpreter VS Code uses by clicking the language mode indicator, or selecting &lt;em&gt;Python: Select Interpreter&lt;/em&gt; from the Command Palette. VS Code supports &lt;a href=&quot;https://code.visualstudio.com/docs/python/editing#_formatterspecific-settings&quot;&gt;formatting&lt;/a&gt; using &lt;code&gt;pep8&lt;/code&gt; by default, but you can select &lt;code&gt;black&lt;/code&gt; or &lt;code&gt;yapf&lt;/code&gt; if you wish.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-language-indicator.6b451f861feb.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/vscode-language-indicator.6b451f861feb.gif&quot; width=&quot;2100&quot; height=&quot;1633&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-language-indicator.6b451f861feb.gif&amp;amp;w=525&amp;amp;sig=5354e0a6b072e64b52bcc32d750b21697fdcae87 525w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-language-indicator.6b451f861feb.gif&amp;amp;w=1050&amp;amp;sig=c6d9b0575e3c1b5d0073be45cebf0db41eb99b47 1050w, https://files.realpython.com/media/vscode-language-indicator.6b451f861feb.gif 2100w&quot; sizes=&quot;75vw&quot; alt=&quot;Using the language indicator in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s add the rest of the Sieve code now. To see IntelliSense at work, type this code directly rather than cut and paste, and you should see something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/typing-sieve-code.3d2ea083d917.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/typing-sieve-code.3d2ea083d917.gif&quot; width=&quot;1109&quot; height=&quot;676&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/typing-sieve-code.3d2ea083d917.gif&amp;amp;w=277&amp;amp;sig=f76fbf86e0441ea2d763609440b32ce0db058fff 277w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/typing-sieve-code.3d2ea083d917.gif&amp;amp;w=554&amp;amp;sig=0a4cdbb10d4e08f81fd8193a39452d293584b3b7 554w, https://files.realpython.com/media/typing-sieve-code.3d2ea083d917.gif 1109w&quot; sizes=&quot;75vw&quot; alt=&quot;Typing the Sieve of Eratosthenes Python code&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the full code for a basic Sieve of Eratosthenes:&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;sieve&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&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;2&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sieve&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;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;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;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;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;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sieve&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;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you type this code, VS Code automatically indents the lines under &lt;code&gt;for&lt;/code&gt; and &lt;code&gt;if&lt;/code&gt; statements for you properly, adds closing parentheses, and makes suggestions for you. That&amp;rsquo;s the power of IntelliSense working for you.&lt;/p&gt;
&lt;h3 id=&quot;running-python-code&quot;&gt;Running Python Code&lt;/h3&gt;
&lt;p&gt;Now that the code is complete, you can run it. There is no need to leave the editor to do this: Visual Studio Code can run this program directly in the editor. Save the file (using &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-s&quot;&gt;S&lt;/kbd&gt;&lt;/span&gt;), then right-click in the editor window and select &lt;em&gt;Run Python File in Terminal&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-run-python-file.d6a0255cd190.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/vscode-run-python-file.d6a0255cd190.gif&quot; width=&quot;1050&quot; height=&quot;817&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-run-python-file.d6a0255cd190.gif&amp;amp;w=262&amp;amp;sig=e6918683593e94f2a1d3b132924534bab19eeb19 262w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-run-python-file.d6a0255cd190.gif&amp;amp;w=525&amp;amp;sig=2754cfbd2db51d1edcef7e1c645f6410c854eed6 525w, https://files.realpython.com/media/vscode-run-python-file.d6a0255cd190.gif 1050w&quot; sizes=&quot;75vw&quot; alt=&quot;Running Python code in VS Code&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You should see the Terminal pane appear at the bottom of the window, with your code output showing.&lt;/p&gt;
&lt;h3 id=&quot;python-linting-support&quot;&gt;Python Linting Support&lt;/h3&gt;
&lt;p&gt;You may have seen a pop up appear while you were typing, stating that linting was not available. You can quickly install linting support from that pop up, which defaults to PyLint. VS Code also supports other linters. Here&amp;rsquo;s the complete list at the time of this writing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pylint&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flake8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mypy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pydocstyle&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pep8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prospector&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pyllama&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bandit&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://code.visualstudio.com/docs/python/linting&quot;&gt;Python linting page&lt;/a&gt; has complete details on how to setup each linter.&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 choice of linter is a project workspace setting, and not a global user setting.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;editing-an-existing-python-project&quot;&gt;Editing an Existing Python Project&lt;/h2&gt;
&lt;p&gt;In the Sieve of Eratosthenes example, you created a single Python file. That&amp;rsquo;s great as an example, but many times, you&amp;rsquo;ll create larger projects and work on them over a longer period of time. A typical new project work flow might look like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a folder to hold the project (which may include a new GitHub project)&lt;/li&gt;
&lt;li&gt;Change to the new folder&lt;/li&gt;
&lt;li&gt;Create the initial Python code using the command &lt;code&gt;code filename.py&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using Visual Studio Code on a Python project (as opposed to a single Python file) opens up tons more functionality that lets VS Code truly shine. Let&amp;rsquo;s take a look at how it works with a larger project.&lt;/p&gt;
&lt;p&gt;Late in the previous millennium, when I was a much younger programmer, I wrote a calculator program that parsed equations written in infix notation, using an adaptation of Edsger Dijkstra&amp;rsquo;s &lt;a href=&quot;https://en.wikipedia.org/wiki/Shunting-yard_algorithm&quot;&gt;shunting yard algorithm&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To demonstrate the project-focused features of Visual Studio Code, I began recreating the shunting yard algorithm as an &lt;a href=&quot;https://github.com/JFincher42/PyEval&quot;&gt;equation evaluation library&lt;/a&gt; in Python. To continue following along, feel free to clone the repo locally.&lt;/p&gt;
&lt;p&gt;Once the folder is created locally, you can open the entire folder in VS Code quickly. My preferred method (as mentioned above) is modified as follows, since I already have the folder and basic files created:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /path/to/project
code .
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;VS Code understands, and will use, any &lt;a href=&quot;https://virtualenv.pypa.io/en/latest/&quot;&gt;virtualenv&lt;/a&gt;, &lt;a href=&quot;https://pipenv.readthedocs.io/en/latest/&quot;&gt;pipenv&lt;/a&gt;, or &lt;a href=&quot;https://conda.io/docs/&quot;&gt;conda&lt;/a&gt; environments it sees when opened this way. You don&amp;rsquo;t even need to start the virtual environment first! You can even open a folder from the UI, using &lt;em&gt;File, Open Folder&lt;/em&gt; from the menu, &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-k&quot;&gt;K&lt;/kbd&gt;&lt;/span&gt;, &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-o&quot;&gt;O&lt;/kbd&gt;&lt;/span&gt; from the keyboard, or &lt;em&gt;File:Open Folder&lt;/em&gt; from the Command Palette.&lt;/p&gt;
&lt;p&gt;For my equation eval library project, here&amp;rsquo;s what I see:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-pyeval-library-folder.964b91919d5b.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/vscode-pyeval-library-folder.964b91919d5b.png&quot; width=&quot;2136&quot; height=&quot;1673&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-pyeval-library-folder.964b91919d5b.png&amp;amp;w=534&amp;amp;sig=24d4908b6f41360571516139e23b9f36ac1af2f6 534w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-pyeval-library-folder.964b91919d5b.png&amp;amp;w=1068&amp;amp;sig=3bb08b3279ad2dd18655abdbdfba02a599e9ee5e 1068w, https://files.realpython.com/media/vscode-pyeval-library-folder.964b91919d5b.png 2136w&quot; sizes=&quot;75vw&quot; alt=&quot;PyEval folder open in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When Visual Studio Code opens the folder, it also opens the files you last had opened. (This is configurable.) You can open, edit, run, and debug any file listed. The Explorer view in the Activity Bar on the left gives you a view of all the files in the folder and shows how many unsaved files exist in the current set of tabs.&lt;/p&gt;
&lt;h2 id=&quot;testing-support&quot;&gt;Testing Support&lt;/h2&gt;
&lt;p&gt;VS Code can automatically recognize &lt;a href=&quot;https://code.visualstudio.com/docs/python/unit-testing&quot;&gt;existing Python tests&lt;/a&gt; written in the &lt;code&gt;unittest&lt;/code&gt; framework, or the &lt;code&gt;pytest&lt;/code&gt; or &lt;code&gt;Nose&lt;/code&gt; frameworks if those frameworks are installed in the current environment. I have a &lt;a href=&quot;https://realpython.com/python-testing/&quot;&gt;unit test&lt;/a&gt; written in &lt;code&gt;unittest&lt;/code&gt; for the equation eval library, which you can use for this example.&lt;/p&gt;
&lt;p&gt;To run your existing unit tests, from any Python file in the project, right-click and select &lt;em&gt;Run Current Unit Test File&lt;/em&gt;. You&amp;rsquo;ll be prompted to specify the test framework, where in the project to search for tests, and the filename pattern your tests utilize.&lt;/p&gt;
&lt;p&gt;All of these are saved as workspace settings in your local &lt;code&gt;.vscode/settings.json&lt;/code&gt; file and can be modified there. For this equation project, you select &lt;code&gt;unittest&lt;/code&gt;, the current folder, and the pattern &lt;code&gt;*_test.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once the test framework is set up and the tests have been discovered, you can run all your tests by clicking &lt;em&gt;Run Tests&lt;/em&gt; on the Status Bar and selecting an option from the Command Palette:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-run-tests.e91253ad83fe.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/vscode-run-tests.e91253ad83fe.gif&quot; width=&quot;1050&quot; height=&quot;817&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-run-tests.e91253ad83fe.gif&amp;amp;w=262&amp;amp;sig=8475272b30fad1e08f1af1b23702ebe07c33662a 262w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-run-tests.e91253ad83fe.gif&amp;amp;w=525&amp;amp;sig=94ae0ab87d14e33a26bb20a59438410c2312dbba 525w, https://files.realpython.com/media/vscode-run-tests.e91253ad83fe.gif 1050w&quot; sizes=&quot;75vw&quot; alt=&quot;Running Python unit tests in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can even run individual tests by opening the test file in VS Code, clicking &lt;em&gt;Run Tests&lt;/em&gt; on the Status Bar, and selecting the &lt;em&gt;Run Unit Test Method&amp;hellip;&lt;/em&gt; and the specific test to run. This makes it trivial to address individual test failures and re-run only failed tests, which is a huge time-saver! Test results are shown in the &lt;em&gt;Output&lt;/em&gt; pane under &lt;em&gt;Python Test Log&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;debugging-support&quot;&gt;Debugging Support&lt;/h2&gt;
&lt;p&gt;Even though VS Code is a code editor, &lt;a href=&quot;https://code.visualstudio.com/docs/python/debugging&quot;&gt;debugging Python&lt;/a&gt; directly within VS Code is possible. VS Code offers many of the features you would expect from a good code debugger, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic variable tracking&lt;/li&gt;
&lt;li&gt;Watch expressions&lt;/li&gt;
&lt;li&gt;Breakpoints&lt;/li&gt;
&lt;li&gt;Call stack inspection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see them all as part of the &lt;em&gt;Debug&lt;/em&gt; view on the Activity Bar:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-debug-screen.647a68278472.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/vscode-debug-screen.647a68278472.png&quot; width=&quot;2136&quot; height=&quot;1673&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-debug-screen.647a68278472.png&amp;amp;w=534&amp;amp;sig=d4d4d0c95797069532b5d18e838d99b4582ca064 534w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-debug-screen.647a68278472.png&amp;amp;w=1068&amp;amp;sig=1abd42299bf8a2895b0ee4ea24fa0c7da44b9694 1068w, https://files.realpython.com/media/vscode-debug-screen.647a68278472.png 2136w&quot; sizes=&quot;75vw&quot; alt=&quot;Finding the Debug UI in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The debugger can control Python apps running in the built-in terminal or an external terminal instance. It can attach to an already running Python instances, and can even debug &lt;a href=&quot;https://realpython.com/tutorials/django/&quot;&gt;Django&lt;/a&gt; and &lt;a href=&quot;https://realpython.com/tutorials/flask/&quot;&gt;Flask&lt;/a&gt; apps.&lt;/p&gt;
&lt;p&gt;Debugging code in a single Python file is as simple as starting the debugger using &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-f5&quot;&gt;F5&lt;/kbd&gt;&lt;/span&gt;. You use &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-f10&quot;&gt;F10&lt;/kbd&gt;&lt;/span&gt; and &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-f11&quot;&gt;F11&lt;/kbd&gt;&lt;/span&gt; to step over and into functions respectively, and &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-shift&quot;&gt;Shift&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-f5&quot;&gt;F5&lt;/kbd&gt;&lt;/span&gt; to exit the debugger. Breakpoints are set using &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-f9&quot;&gt;F9&lt;/kbd&gt;&lt;/span&gt;, or using the mouse by clicking in the left margin in the editor window.&lt;/p&gt;
&lt;p&gt;Before you start debugging more complicated projects, including Django or Flask applications, you need to setup and then select a debug configuration. Setting up the debug configuration is relatively straightforward. From the &lt;em&gt;Debug&lt;/em&gt; view, select the &lt;em&gt;Configuration&lt;/em&gt; drop-down, then &lt;em&gt;Add Configuration&lt;/em&gt;, and select &lt;em&gt;Python&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-debug-add-configuration.cee71411723c.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/vscode-debug-add-configuration.cee71411723c.png&quot; width=&quot;2136&quot; height=&quot;1673&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-debug-add-configuration.cee71411723c.png&amp;amp;w=534&amp;amp;sig=80cd9e9631a930e70dfe56904b854257e9f2026a 534w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-debug-add-configuration.cee71411723c.png&amp;amp;w=1068&amp;amp;sig=9c7712594c1d98a63bfa8953a30e343c76207db0 1068w, https://files.realpython.com/media/vscode-debug-add-configuration.cee71411723c.png 2136w&quot; sizes=&quot;75vw&quot; alt=&quot;Adding a new debug configuration to VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Visual Studio Code will create a debug configuration file under the current folder called &lt;code&gt;.vscode/launch.json&lt;/code&gt;, which allows you to setup specific &lt;a href=&quot;https://code.visualstudio.com/docs/python/debugging#_standard-configuration-and-options&quot;&gt;Python configurations&lt;/a&gt; as well as settings for &lt;a href=&quot;https://code.visualstudio.com/docs/python/debugging#_debugging-specific-app-types_&quot;&gt;debugging specific apps&lt;/a&gt;, like Django and Flask.&lt;/p&gt;
&lt;p&gt;You can even perform remote debugging, and debug Jinja and Django templates. Close the &lt;code&gt;launch.json&lt;/code&gt; file in the editor and select the proper configuration for your application from the &lt;em&gt;Configuration&lt;/em&gt; drop-down.&lt;/p&gt;
&lt;h2 id=&quot;git-integration&quot;&gt;Git Integration&lt;/h2&gt;
&lt;p&gt;VS Code has built-in support for &lt;a href=&quot;https://code.visualstudio.com/docs/editor/versioncontrol&quot;&gt;source control management&lt;/a&gt;, and ships with support for Git and GitHub right out of the box. You can install support for other SCM&amp;rsquo;s in VS Code, and use them side by side. Source control is accessible from the &lt;em&gt;Source Control&lt;/em&gt; view:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-source-control.6b333fd5b16c.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/vscode-source-control.6b333fd5b16c.png&quot; width=&quot;2136&quot; height=&quot;1673&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-source-control.6b333fd5b16c.png&amp;amp;w=534&amp;amp;sig=48c264e9b8c21343a50cea4324c2880a1d326538 534w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-source-control.6b333fd5b16c.png&amp;amp;w=1068&amp;amp;sig=f3c4eed287bccbf9663c708f2d065f6fdbc33c1c 1068w, https://files.realpython.com/media/vscode-source-control.6b333fd5b16c.png 2136w&quot; sizes=&quot;75vw&quot; alt=&quot;Source Control UI in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If your project folder contains a &lt;code&gt;.git&lt;/code&gt; folder, VS Code automatically turns on the full range of &lt;a href=&quot;https://code.visualstudio.com/docs/editor/versioncontrol#_git-support&quot;&gt;Git/GitHub&lt;/a&gt; functionality. Here are some of the many tasks you can perform:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/editor/versioncontrol#_commit&quot;&gt;Commit files&lt;/a&gt; to Git&lt;/li&gt;
&lt;li&gt;Push changes to, and pull changes from, &lt;a href=&quot;https://code.visualstudio.com/docs/editor/versioncontrol#_remotes&quot;&gt;remote repos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Check-out existing or create new &lt;a href=&quot;https://code.visualstudio.com/docs/editor/versioncontrol#_branches-and-tags&quot;&gt;branches and tags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;View and resolve &lt;a href=&quot;https://code.visualstudio.com/docs/editor/versioncontrol#_merge-conflicts&quot;&gt;merge conflicts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/editor/versioncontrol#_viewing-diffs&quot;&gt;View diffs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this functionality is available directly from the VS Code UI: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-git-commands.487bd9e2de71.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/vscode-git-commands.487bd9e2de71.png&quot; width=&quot;2136&quot; height=&quot;1673&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-git-commands.487bd9e2de71.png&amp;amp;w=534&amp;amp;sig=7a7684c5c87452cc970db16d291fcd5942cc3004 534w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-git-commands.487bd9e2de71.png&amp;amp;w=1068&amp;amp;sig=af30875bf891f2abee96ba670ce02dad9b2de698 1068w, https://files.realpython.com/media/vscode-git-commands.487bd9e2de71.png 2136w&quot; sizes=&quot;75vw&quot; alt=&quot;Git Commands in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;VS Code will also recognize changes made outside the editor and behave appropriately.&lt;/p&gt;
&lt;p&gt;Committing your recent changes within VS Code is a fairly straightforward process. Modified files are shown in the &lt;em&gt;Source Control&lt;/em&gt; view with an &lt;em&gt;M&lt;/em&gt; marker, while new untracked files are marked with a &lt;em&gt;U&lt;/em&gt;. Stage your changes by hovering over the file and then clicking the plus sign (&lt;em&gt;+&lt;/em&gt;). Add a commit message at the top of the view, and then click the check mark to commit the changes:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode-commit-changes.781a88f03210.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/vscode-commit-changes.781a88f03210.png&quot; width=&quot;2136&quot; height=&quot;1673&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-commit-changes.781a88f03210.png&amp;amp;w=534&amp;amp;sig=833faa87f9cf7c491d6cc400b7f7353ee0285985 534w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode-commit-changes.781a88f03210.png&amp;amp;w=1068&amp;amp;sig=020902e1302c31bbe6bad647a37b49bc8bda102b 1068w, https://files.realpython.com/media/vscode-commit-changes.781a88f03210.png 2136w&quot; sizes=&quot;75vw&quot; alt=&quot;Committing changes in VSCode&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can push local commits to GitHub from within VS Code as well. Select &lt;em&gt;Sync&lt;/em&gt; from the &lt;em&gt;Source Control&lt;/em&gt; view menu, or click &lt;em&gt;Synchronize Changes&lt;/em&gt; on the &lt;a href=&quot;https://code.visualstudio.com/docs/editor/versioncontrol#_git-status-bar-actions&quot;&gt;status bar&lt;/a&gt; next to the branch indicator.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Visual Studio Code is one of the coolest general purpose editors and a great candidate for Python development. In this article, you learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to install VS Code on any platform&lt;/li&gt;
&lt;li&gt;How to find and install extensions to enable Python-specific features&lt;/li&gt;
&lt;li&gt;How VS Code makes writing a simple Python application easier&lt;/li&gt;
&lt;li&gt;How to run and debug existing Python programs within VS Code&lt;/li&gt;
&lt;li&gt;How to work with Git and GitHub repositories from VS Code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Visual Studio Code has become my default editor for Python and other tasks, and I hope you give it a chance to become yours as well.&lt;/p&gt;
&lt;p&gt;If you have questions or comments, please reach out in the comments below. There is also a lot more information at the &lt;a href=&quot;https://code.visualstudio.com&quot;&gt;Visual Studio Code website&lt;/a&gt; than we could cover here.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The author sends thanks to &lt;a href=&quot;https://twitter.com/qubitron&quot;&gt;Dan Taylor&lt;/a&gt; from the Visual Studio Code team at Microsoft for his time and invaluable input in this article.&lt;/em&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>Python &quot;for&quot; Loops (Definite Iteration)</title>
      <id>https://realpython.com/python-for-loop/</id>
      <link href="https://realpython.com/python-for-loop/"/>
      <updated>2019-01-30T06:00:00+00:00</updated>
      <summary>In this introductory tutorial, you&#39;ll learn all about how to perform definite iteration with Python for loops. You’ll see how other programming languages implement definite iteration, learn about iterables and iterators, and tie it all together to learn about Python’s for loop.</summary>
      <content type="html">
        &lt;p&gt;This tutorial will show you how to perform &lt;strong&gt;definite iteration&lt;/strong&gt; with a Python &lt;code&gt;for&lt;/code&gt; loop.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://realpython.com/python-while-loop/&quot;&gt;previous tutorial&lt;/a&gt; in this introductory series, you learned the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Repetitive execution of the same block of code over and over is referred to as &lt;strong&gt;iteration&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;There are two types of iteration:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Definite&lt;/strong&gt; iteration, in which the number of repetitions is specified explicitly in advance&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Indefinite&lt;/strong&gt; iteration, in which the code block executes until some condition is met&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In Python, indefinite iteration is performed with a &lt;code&gt;while&lt;/code&gt; loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Here&amp;rsquo;s what you&amp;rsquo;ll cover in this tutorial:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You&amp;rsquo;ll start with a comparison of some different paradigms used by programming languages to implement definite iteration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then you will learn about &lt;strong&gt;iterables&lt;/strong&gt; and &lt;strong&gt;iterators&lt;/strong&gt;, two concepts that form the basis of definite iteration in Python.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, you&amp;rsquo;ll tie it all together and learn about Python&amp;rsquo;s &lt;code&gt;for&lt;/code&gt; loops.&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-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-survey-of-definite-iteration-in-programming&quot;&gt;A Survey of Definite Iteration in Programming&lt;/h2&gt;
&lt;p&gt;Definite iteration loops are frequently referred to as &lt;strong&gt;&lt;code&gt;for&lt;/code&gt;&lt;/strong&gt; loops because &lt;code&gt;for&lt;/code&gt; is the keyword that is used to introduce them in nearly all programming languages, including Python.&lt;/p&gt;
&lt;p&gt;Historically, programming languages have offered a few assorted flavors of &lt;code&gt;for&lt;/code&gt; loop. These are briefly described in the following sections.&lt;/p&gt;
&lt;h3 id=&quot;numeric-range-loop&quot;&gt;Numeric Range Loop&lt;/h3&gt;
&lt;p&gt;The most basic &lt;code&gt;for&lt;/code&gt; loop is a simple numeric range statement with start and end values.  The exact format varies depending on the language but typically looks something like this:&lt;/p&gt;
&lt;div class=&quot;highlight basic&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, the body of the loop is executed ten times.  The variable &lt;code&gt;i&lt;/code&gt; assumes the value &lt;code&gt;1&lt;/code&gt; on the first iteration, &lt;code&gt;2&lt;/code&gt; on the second, and so on.  This sort of &lt;code&gt;for&lt;/code&gt; loop is used in the languages BASIC, Algol, and Pascal.&lt;/p&gt;
&lt;h3 id=&quot;three-expression-loop&quot;&gt;Three-Expression Loop&lt;/h3&gt;
&lt;p&gt;Another form of &lt;code&gt;for&lt;/code&gt; loop popularized by the C programming language contains three parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An initialization&lt;/li&gt;
&lt;li&gt;An expression specifying an ending condition&lt;/li&gt;
&lt;li&gt;An action to be performed at the end of each iteration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This type of has the following form:&lt;/p&gt;
&lt;div class=&quot;highlight c&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&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;n&quot;&gt;i&lt;/span&gt;&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;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;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;Technical Note:&lt;/strong&gt; In the C programming language, &lt;code&gt;i++&lt;/code&gt; increments the variable &lt;code&gt;i&lt;/code&gt;. It is roughly equivalent to &lt;code&gt;i += 1&lt;/code&gt; in Python.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This loop is interpreted as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initialize &lt;code&gt;i&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Continue looping as long as &lt;code&gt;i &amp;lt;= 10&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Increment &lt;code&gt;i&lt;/code&gt; by &lt;code&gt;1&lt;/code&gt; after each loop iteration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Three-expression &lt;code&gt;for&lt;/code&gt; loops are popular because the expressions specified for the three parts can be nearly anything, so this has quite a bit more flexibility than the simpler numeric range form shown above.  These &lt;code&gt;for&lt;/code&gt; loops are also featured in the C++, Java, PHP, and Perl languages.&lt;/p&gt;
&lt;h3 id=&quot;collection-based-or-iterator-based-loop&quot;&gt;Collection-Based or Iterator-Based Loop&lt;/h3&gt;
&lt;p&gt;This type of loop iterates over a collection of objects, rather than specifying numeric values or 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;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;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;collection&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;loop&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each time through the loop, the variable &lt;code&gt;i&lt;/code&gt; takes on the value of the next object in &lt;code&gt;&amp;lt;collection&amp;gt;&lt;/code&gt;.  This type of &lt;code&gt;for&lt;/code&gt; loop is arguably the most generalized and abstract.  Perl and PHP also support this type of loop, but it is introduced by the keyword &lt;code&gt;foreach&lt;/code&gt; instead of &lt;code&gt;for&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt; See the &lt;a href=&quot;https://en.wikipedia.org/wiki/For_loop&quot;&gt;For loop&lt;/a&gt; Wikipedia page for an in-depth look at the implementation of definite iteration across programming languages.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;the-python-for-loop&quot;&gt;The Python &lt;code&gt;for&lt;/code&gt; Loop&lt;/h2&gt;
&lt;p&gt;Of the loop types listed above, Python only implements the last:  collection-based iteration.  At first blush, that may seem like a raw deal, but rest assured that Python&amp;rsquo;s implementation of definite iteration is so versatile that you won&amp;rsquo;t end up feeling cheated!&lt;/p&gt;
&lt;p&gt;Shortly, you&amp;rsquo;ll dig into the guts of Python&amp;rsquo;s &lt;code&gt;for&lt;/code&gt; loop in detail.  But for now, let&amp;rsquo;s start with a quick prototype and example, just to get acquainted.&lt;/p&gt;
&lt;p&gt;Python&amp;rsquo;s &lt;code&gt;for&lt;/code&gt; loop 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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterable&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;iterable&amp;gt;&lt;/code&gt; is a collection of objects&amp;mdash;for example, a list or tuple.  The &lt;code&gt;&amp;lt;statement(s)&amp;gt;&lt;/code&gt; in the loop body are denoted by indentation, as with all Python control structures, and are executed once for each item in &lt;code&gt;&amp;lt;iterable&amp;gt;&lt;/code&gt;.  The loop variable &lt;code&gt;&amp;lt;var&amp;gt;&lt;/code&gt; takes on the value of the next element in &lt;code&gt;&amp;lt;iterable&amp;gt;&lt;/code&gt; each time through the loop.&lt;/p&gt;
&lt;p&gt;Here is a representative 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;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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;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;i&lt;/span&gt;&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;bar&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;baz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, &lt;code&gt;&amp;lt;iterable&amp;gt;&lt;/code&gt; is the list &lt;code&gt;a&lt;/code&gt;, and &lt;code&gt;&amp;lt;var&amp;gt;&lt;/code&gt; is the variable &lt;code&gt;i&lt;/code&gt;.  Each time through the loop, &lt;code&gt;i&lt;/code&gt; takes on a successive item in &lt;code&gt;a&lt;/code&gt;, so &lt;code&gt;print()&lt;/code&gt; displays the values &lt;code&gt;&#39;foo&#39;&lt;/code&gt;, &lt;code&gt;&#39;bar&#39;&lt;/code&gt;, and &lt;code&gt;&#39;baz&#39;&lt;/code&gt;, respectively. A &lt;code&gt;for&lt;/code&gt; loop like this is the Pythonic way to process the items in an iterable.&lt;/p&gt;
&lt;p&gt;But what exactly is an iterable?  Before examining &lt;code&gt;for&lt;/code&gt; loops further, it will be beneficial to delve more deeply into what iterables are in Python.&lt;/p&gt;
&lt;h3 id=&quot;iterables&quot;&gt;Iterables&lt;/h3&gt;
&lt;p&gt;In Python, &lt;strong&gt;iterable&lt;/strong&gt; means an object can be used in iteration.  The term is used as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;An adjective:&lt;/strong&gt; An object may be described as iterable. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A noun:&lt;/strong&gt; An object may be characterized as an iterable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If an object is iterable, it can be passed to the built-in Python function &lt;code&gt;iter()&lt;/code&gt;, which returns something called an &lt;strong&gt;iterator&lt;/strong&gt;. Yes, the terminology gets a bit repetitive. Hang in there. It all works out in the end.&lt;/p&gt;
&lt;p&gt;Each of the objects in the following example is an iterable and returns some type of iterator when passed to &lt;code&gt;iter()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;foobar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;                             &lt;span class=&quot;c1&quot;&gt;# String&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;str_iterator object at 0x036E2750&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;iter&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;c1&quot;&gt;# List&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;list_iterator object at 0x036E27D0&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;iter&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;c1&quot;&gt;# Tuple&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;tuple_iterator object at 0x036E27F0&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;iter&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;c1&quot;&gt;# Set&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;set_iterator object at 0x036DEA08&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;iter&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;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# Dict&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;dict_keyiterator object at 0x036DD990&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These object types, on the other hand, aren&amp;rsquo;t iterable:&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;iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;                                   &lt;span class=&quot;c1&quot;&gt;# Integer&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#26&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;TypeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;&amp;#39;int&amp;#39; object is not iterable&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;iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;                                  &lt;span class=&quot;c1&quot;&gt;# Float&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#27&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;TypeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;&amp;#39;float&amp;#39; object is not iterable&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;iter&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;c1&quot;&gt;# Built-in function&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#28&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;iter&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;gr&quot;&gt;TypeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;&amp;#39;builtin_function_or_method&amp;#39; object is not iterable&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All the data types you have encountered so far that are collection or container types are iterable.  These include the &lt;a href=&quot;https://realpython.com/python-strings/&quot;&gt;string&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/python-lists-tuples/#python-lists&quot;&gt;list&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/python-lists-tuples/#python-tuples&quot;&gt;tuple&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/python-dicts/&quot;&gt;dict&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/python-sets/&quot;&gt;set&lt;/a&gt;, and &lt;a href=&quot;https://realpython.com/python-sets/#frozen-sets&quot;&gt;frozenset&lt;/a&gt; types.&lt;/p&gt;
&lt;p&gt;But these are by no means the only types that you can iterate over.  Many objects that are built into Python or defined in modules are designed to be iterable.  For example, open files in Python are iterable.  As you will see soon in the tutorial on file I/O, iterating over an open file object reads data from the file.&lt;/p&gt;
&lt;p&gt;In fact, almost any object in Python can be made iterable.  Even user-defined objects can be designed in such a way that they can be iterated over.  (You will find out how that is done in the upcoming article on object-oriented programming.)&lt;/p&gt;
&lt;h3 id=&quot;iterators&quot;&gt;Iterators&lt;/h3&gt;
&lt;p&gt;Okay, now you know what it means for an object to be iterable, and you know how to use &lt;code&gt;iter()&lt;/code&gt; to obtain an iterator from it.  Once you&amp;rsquo;ve got an iterator, what can you do with it?&lt;/p&gt;
&lt;p&gt;An iterator is essentially a value producer that yields successive values from its associated iterable object.  The built-in function &lt;code&gt;next()&lt;/code&gt; is used to obtain the next value from in iterator.&lt;/p&gt;
&lt;p&gt;Here is an example using the same list as above:&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;n&quot;&gt;itr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;iter&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;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;list_iterator object at 0x031EFD10&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;foo&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;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;bar&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;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, &lt;code&gt;a&lt;/code&gt; is an iterable list and &lt;code&gt;itr&lt;/code&gt; is the associated iterator, obtained with &lt;code&gt;iter()&lt;/code&gt;.  Each &lt;code&gt;next(itr)&lt;/code&gt; call obtains the next value from &lt;code&gt;itr&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Notice how an iterator retains its state internally.  It knows which values have been obtained already, so when you call &lt;code&gt;next()&lt;/code&gt;, it knows what value to return next.&lt;/p&gt;
&lt;p&gt;What happens when the iterator runs out of values?  Let&amp;rsquo;s make one more &lt;code&gt;next()&lt;/code&gt; call on the iterator above:&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;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;pyshell#10&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
    &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;itr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;StopIteration&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If all the values from an iterator have been returned already, a subsequent &lt;code&gt;next()&lt;/code&gt; call raises a &lt;code&gt;StopIteration&lt;/code&gt; exception.  Any further attempts to obtain values from the iterator will fail.&lt;/p&gt;
&lt;p&gt;You can only obtain values from an iterator in one direction.  You can&amp;rsquo;t go backward. There is no &lt;code&gt;prev()&lt;/code&gt; function.  But you can define two independent iterators on the same iterable object:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;foo&amp;#39;, &amp;#39;bar&amp;#39;, &amp;#39;baz&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;itr1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;iter&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;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;iter&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;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;&amp;#39;foo&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;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;bar&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;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;baz&amp;#39;&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;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even when iterator &lt;code&gt;itr1&lt;/code&gt; is already at the end of the list, &lt;code&gt;itr2&lt;/code&gt; is still at the beginning.  Each iterator maintains its own internal state, independent of the other.&lt;/p&gt;
&lt;p&gt;If you want to grab all the values from an iterator at once, you can use the built-in &lt;code&gt;list()&lt;/code&gt; function. Among other possible uses, &lt;code&gt;list()&lt;/code&gt; takes an iterator as its argument, and returns a list consisting of all the values that the iterator yielded:&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;n&quot;&gt;itr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;iter&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;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;foo&amp;#39;, &amp;#39;bar&amp;#39;, &amp;#39;baz&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Similarly, the built-in &lt;code&gt;tuple()&lt;/code&gt; and &lt;code&gt;set()&lt;/code&gt; functions return a tuple and a set, respectively, from all the values an iterator yields:&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;n&quot;&gt;itr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;iter&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;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(&amp;#39;foo&amp;#39;, &amp;#39;bar&amp;#39;, &amp;#39;baz&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;itr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;iter&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;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;baz&amp;#39;, &amp;#39;foo&amp;#39;, &amp;#39;bar&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It isn&amp;rsquo;t necessarily advised to make a habit of this.  Part of the elegance of iterators is that they are &amp;ldquo;lazy.&amp;rdquo;  That means that when you create an iterator, it doesn&amp;rsquo;t generate all the items it can yield just then.  It waits until you ask for them with &lt;code&gt;next()&lt;/code&gt;.  Items are not created until they are requested.&lt;/p&gt;
&lt;p&gt;When you use &lt;code&gt;list()&lt;/code&gt;, &lt;code&gt;tuple()&lt;/code&gt;, or the like, you are forcing the iterator to generate all its values at once, so they can all be returned.  If the total number of objects the iterator returns is very large, that may take a long time.&lt;/p&gt;
&lt;p&gt;In fact, it is possible to create an iterator in Python that returns an endless series of objects. (You will learn how to do this in upcoming tutorials on generator functions and &lt;code&gt;itertools&lt;/code&gt;.) If you try to grab all the values at once from an endless iterator, the program will &lt;a href=&quot;https://en.wikipedia.org/wiki/Hang_(computing)&quot;&gt;hang&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-guts-of-the-python-for-loop&quot;&gt;The Guts of the Python &lt;code&gt;for&lt;/code&gt; Loop&lt;/h2&gt;
&lt;p&gt;You now have been introduced to all the concepts you need to fully understand how Python&amp;rsquo;s &lt;code&gt;for&lt;/code&gt; loop works.  Before proceeding, let&amp;rsquo;s review the relevant terms:&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;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Iteration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The process of looping through the objects or items in a collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Iterable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An object (or the adjective used to describe an object) that can be iterated over&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Iterator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The object that produces successive items or values from its associated iterable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;iter()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The built-in function used to obtain an iterator from an iterable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Now, consider again the simple &lt;code&gt;for&lt;/code&gt; loop presented at the start of this tutorial:&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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;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;i&lt;/span&gt;&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;bar&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;baz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This loop can be described entirely in terms of the concepts you have just learned about.  To carry out the iteration this &lt;code&gt;for&lt;/code&gt; loop describes, Python does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Calls &lt;code&gt;iter()&lt;/code&gt; to obtain an iterator for &lt;code&gt;a&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;next()&lt;/code&gt; repeatedly to obtain each item from the iterator in turn&lt;/li&gt;
&lt;li&gt;Terminates the loop when &lt;code&gt;next()&lt;/code&gt; raises the &lt;code&gt;StopIteration&lt;/code&gt; exception&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The loop body is executed once for each item &lt;code&gt;next()&lt;/code&gt; returns, with loop variable &lt;code&gt;i&lt;/code&gt; set to the given item for each iteration.&lt;/p&gt;
&lt;p&gt;This sequence of events is summarized 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.ba63222d63f5.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.ba63222d63f5.png&quot; width=&quot;1062&quot; height=&quot;996&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/t.ba63222d63f5.png&amp;amp;w=265&amp;amp;sig=de0435555a10dcf6eabc2cdfab0389f61a54c722 265w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/t.ba63222d63f5.png&amp;amp;w=531&amp;amp;sig=4461dc7eef0de5b223c79c01ff75ffb9339bba8d 531w, https://files.realpython.com/media/t.ba63222d63f5.png 1062w&quot; sizes=&quot;75vw&quot; alt=&quot;Python for loop diagram&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Schematic Diagram of a Python for Loop&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Perhaps this seems like a lot of unnecessary monkey business, but the benefit is substantial.  Python treats looping over all iterables in exactly this way, and in Python, iterables and iterators abound:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Many built-in and library objects are iterable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There is a Standard Library module called &lt;code&gt;itertools&lt;/code&gt; containing many functions that return iterables.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;User-defined objects created with Python&amp;rsquo;s object-oriented capability can be made to be iterable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Python features a construct called a generator that allows you to create your own iterator in a simple, straightforward way.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will discover more about all the above throughout this series.  They can all be the target of a &lt;code&gt;for&lt;/code&gt; loop, and the syntax is the same across the board.  It&amp;rsquo;s elegant in its simplicity and eminently versatile.&lt;/p&gt;
&lt;h2 id=&quot;iterating-through-a-dictionary&quot;&gt;Iterating Through a Dictionary&lt;/h2&gt;
&lt;p&gt;You saw earlier that an iterator can be obtained from a dictionary with &lt;code&gt;iter()&lt;/code&gt;, so you know dictionaries must be iterable.  What happens when you loop through a dictionary?  Let&amp;rsquo;s see:&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;d&lt;/span&gt; &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;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&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;k&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&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;k&lt;/span&gt;&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;bar&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;baz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, when a &lt;code&gt;for&lt;/code&gt; loop iterates through a dictionary, the loop variable is assigned to the dictionary&amp;rsquo;s keys.&lt;/p&gt;
&lt;p&gt;To access the dictionary values within the loop, you can make a dictionary reference using the key as usual:&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;k&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&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;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&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;1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also iterate through a dictionary&amp;rsquo;s values directly by using &lt;code&gt;.values()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;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;v&lt;/span&gt;&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;1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In fact, you can iterate through both the keys and values of a dictionary simultaneously.  That is because the loop variable of a &lt;code&gt;for&lt;/code&gt; loop isn&amp;rsquo;t limited to just a single variable.  It can also be a tuple, in which case the assignments are made from the items in the iterable using packing and unpacking, just as with an assignment statement:&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;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;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;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&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;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;span class=&quot;go&quot;&gt;1 2&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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&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;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;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;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&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&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;1 2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3 4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;5 6&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As noted in the tutorial on Python &lt;a href=&quot;https://realpython.com/python-dicts/#built-in-dictionary-methods&quot;&gt;dictionaries&lt;/a&gt;, the dictionary method &lt;code&gt;.items()&lt;/code&gt; effectively returns a list of key/value pairs as tuples:&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;d&lt;/span&gt; &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;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&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;go&quot;&gt;dict_items([(&amp;#39;foo&amp;#39;, 1), (&amp;#39;bar&amp;#39;, 2), (&amp;#39;baz&amp;#39;, 3)])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Thus, the Pythonic way to iterate through a dictionary accessing both the keys and values looks 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;n&quot;&gt;d&lt;/span&gt; &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;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&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;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&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;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;k =&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;, v =&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&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;k = foo , v = 1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;k = bar , v = 2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;k = baz , v = 3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-range-function&quot;&gt;The &lt;code&gt;range()&lt;/code&gt; Function&lt;/h2&gt;
&lt;p&gt;In the first section of this tutorial, you saw a type of &lt;code&gt;for&lt;/code&gt; loop called a &lt;a href=&quot;https://realpython.com/python-for-loop/#numeric-range-loop&quot;&gt;numeric range loop&lt;/a&gt;, in which starting and ending numeric values are specified.  Although this form of &lt;code&gt;for&lt;/code&gt; loop isn&amp;rsquo;t directly built into Python, it is easily arrived at.&lt;/p&gt;
&lt;p&gt;For example, if you wanted to iterate through the values from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;4&lt;/code&gt;, you could simply 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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;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;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;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;span class=&quot;go&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This solution isn&amp;rsquo;t too bad when there are just a few numbers.  But if the number range were much larger, it would become tedious pretty quickly.&lt;/p&gt;
&lt;p&gt;Happily, Python provides a better option&amp;mdash;the built-in &lt;code&gt;range()&lt;/code&gt; function, which returns an iterable that yields a sequence of integers.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;range(&amp;lt;end&amp;gt;)&lt;/code&gt; returns an iterable that yields integers starting with &lt;code&gt;0&lt;/code&gt;, up to but not including &lt;code&gt;&amp;lt;end&amp;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;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;5&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&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;range(0, 5)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;class &amp;#39;range&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code&gt;range()&lt;/code&gt; returns an object of class &lt;code&gt;range&lt;/code&gt;, not a list or tuple of the values.  Because a &lt;code&gt;range&lt;/code&gt; object is an iterable, you can obtain the values by iterating over them with a &lt;code&gt;for&lt;/code&gt; loop:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;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;span class=&quot;go&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You could also snag all the values at once with &lt;code&gt;list()&lt;/code&gt; or &lt;code&gt;tuple()&lt;/code&gt;.  In a REPL session, that can be a convenient way to quickly display what the values are:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[0, 1, 2, 3, 4]&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;tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(0, 1, 2, 3, 4)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, when &lt;code&gt;range()&lt;/code&gt; is used in code that is part of a larger application, it is typically considered poor practice to use &lt;code&gt;list()&lt;/code&gt; or &lt;code&gt;tuple()&lt;/code&gt; in this way.  Like iterators, &lt;code&gt;range&lt;/code&gt; objects are lazy&amp;mdash;the values in the specified range are not generated until they are requested.  Using &lt;code&gt;list()&lt;/code&gt; or &lt;code&gt;tuple()&lt;/code&gt; on a &lt;code&gt;range&lt;/code&gt; object forces all the values to be returned at once.  This is rarely necessary, and if the list is long, it can waste time and memory.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;range(&amp;lt;begin&amp;gt;, &amp;lt;end&amp;gt;, &amp;lt;stride&amp;gt;)&lt;/code&gt; returns an iterable that yields integers starting with &lt;code&gt;&amp;lt;begin&amp;gt;&lt;/code&gt;, up to but not including &lt;code&gt;&amp;lt;end&amp;gt;&lt;/code&gt;.  If specified, &lt;code&gt;&amp;lt;stride&amp;gt;&lt;/code&gt; indicates an amount to skip between values (analogous to the stride value used for string and list slicing):&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&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;20&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;go&quot;&gt;[5, 8, 11, 14, 17]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If &lt;code&gt;&amp;lt;stride&amp;gt;&lt;/code&gt; is omitted, it defaults to &lt;code&gt;1&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&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;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[5, 6, 7, 8, 9]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&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;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[5, 6, 7, 8, 9]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All the parameters specified to &lt;code&gt;range()&lt;/code&gt; must be integers, but any of them can be negative.  Naturally, if &lt;code&gt;&amp;lt;begin&amp;gt;&lt;/code&gt; is greater than &lt;code&gt;&amp;lt;end&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;stride&amp;gt;&lt;/code&gt; must be negative (if you want any results):&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;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;[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&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;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;go&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;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;go&quot;&gt;[5, 4, 3, 2, 1, 0, -1, -2, -3, -4]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Technical Note:&lt;/strong&gt; Strictly speaking, &lt;code&gt;range()&lt;/code&gt; isn&amp;rsquo;t exactly a built-in function.  It is implemented as a callable class that creates an immutable sequence type.  But for practical purposes, it behaves like a built-in function.&lt;/p&gt;
&lt;p&gt;For more information on &lt;code&gt;range()&lt;/code&gt;, see the Real Python article &lt;a href=&quot;https://realpython.com/python-range&quot;&gt;Python&amp;rsquo;s &lt;code&gt;range()&lt;/code&gt; Function (Guide)&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;altering-for-loop-behavior&quot;&gt;Altering &lt;code&gt;for&lt;/code&gt; Loop Behavior&lt;/h2&gt;
&lt;p&gt;You saw in the previous tutorial in this introductory series how execution of a &lt;code&gt;while&lt;/code&gt; loop can be interrupted with &lt;a href=&quot;https://realpython.com/python-while-loop/#interruption-of-loop-iteration&quot;&gt;&lt;code&gt;break&lt;/code&gt; and &lt;code&gt;continue&lt;/code&gt; statements&lt;/a&gt; and modified with an &lt;a href=&quot;https://realpython.com/python-while-loop/#the-else-clause&quot;&gt;&lt;code&gt;else&lt;/code&gt; clause&lt;/a&gt;. These capabilities are available with the &lt;code&gt;for&lt;/code&gt; loop as well.&lt;/p&gt;
&lt;h3 id=&quot;the-break-and-continue-statements&quot;&gt;The &lt;code&gt;break&lt;/code&gt; and &lt;code&gt;continue&lt;/code&gt; Statements&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;break&lt;/code&gt; and &lt;code&gt;continue&lt;/code&gt; work the same way with &lt;code&gt;for&lt;/code&gt; loops as with &lt;code&gt;while&lt;/code&gt; loops. &lt;code&gt;break&lt;/code&gt; terminates the loop completely and proceeds to the first statement following the loop:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;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;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;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;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;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;gp&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;&lt;code&gt;continue&lt;/code&gt; terminates the current iteration and proceeds to the next iteration:&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;i&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;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;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;continue&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;i&lt;/span&gt;&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;qux&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;the-else-clause&quot;&gt;The &lt;code&gt;else&lt;/code&gt; Clause&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;for&lt;/code&gt; loop can have an &lt;code&gt;else&lt;/code&gt; clause as well.  The interpretation is analogous to that of a &lt;code&gt;while&lt;/code&gt; loop. The &lt;code&gt;else&lt;/code&gt; clause will be executed if the loop terminates through exhaustion of the iterable:&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;i&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;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;... &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;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;Done.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Will execute&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;bar&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;baz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;qux&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Done.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;else&lt;/code&gt; clause won&amp;rsquo;t be executed if the list is broken out of with a &lt;code&gt;break&lt;/code&gt; statement:&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;i&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;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;... &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;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;... &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;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;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;Done.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Will not execute&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;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This tutorial presented the &lt;code&gt;for&lt;/code&gt; loop, the workhorse of &lt;strong&gt;definite iteration&lt;/strong&gt; in Python.&lt;/p&gt;
&lt;p&gt;You also learned about the inner workings of &lt;strong&gt;iterables&lt;/strong&gt; and &lt;strong&gt;iterators&lt;/strong&gt;, two important object types that underlie definite iteration, but also figure prominently in a wide variety of other Python code.&lt;/p&gt;
&lt;p&gt;In the next two tutorials in this introductory series, you will shift gears a little and explore how Python programs can interact with the user via &lt;strong&gt;input&lt;/strong&gt; from the keyboard and &lt;strong&gt;output&lt;/strong&gt; to the console.&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-while-loop/&quot;&gt; «&amp;nbsp;Python &quot;while&quot; Loops (Indefinite Iteration)&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;for&quot; Loops (Definite Iteration)&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;Basic Input and Output in Python&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>Jupyter Notebook: An Introduction</title>
      <id>https://realpython.com/jupyter-notebook-introduction/</id>
      <link href="https://realpython.com/jupyter-notebook-introduction/"/>
      <updated>2019-01-28T06:00:00+00:00</updated>
      <summary>In this step-by-step Python tutorial, you learn how to get started with The Jupyter Notebook, an open source web application that you can use to create and share documents that contain live code, equations, visualizations, and text.</summary>
      <content type="html">
        &lt;p&gt;The Jupyter Notebook is an open source web application that you can use to create and share documents that contain live code, equations, visualizations, and text. Jupyter Notebook is maintained by the people at &lt;a href=&quot;http://jupyter.org/&quot;&gt;Project Jupyter&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Jupyter Notebooks are a spin-off project from the IPython project, which used to have an IPython Notebook project itself. The name, Jupyter, comes from the core supported programming languages that it supports: Julia, Python, and R. Jupyter ships with the IPython kernel, which allows you to write your programs in Python, but there are currently over 100 other kernels that you can also use.&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;getting-up-and-running-with-jupyter-notebook&quot;&gt;Getting Up and Running With Jupyter Notebook&lt;/h2&gt;
&lt;p&gt;The Jupyter Notebook is not included with Python, so if you want to try it out, you will need to install Jupyter.&lt;/p&gt;
&lt;p&gt;There are many distributions of the Python language. This article will focus on just two of them for the purposes of installing Jupyter Notebook. The most popular is CPython, which is the reference version of Python that you can get from their &lt;a href=&quot;https://www.python.org/&quot;&gt;website&lt;/a&gt;. It is also assumed that you are using &lt;strong&gt;Python 3&lt;/strong&gt;. &lt;/p&gt;
&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;/h3&gt;
&lt;p&gt;If so, then you can use a handy tool that comes with Python called &lt;strong&gt;pip&lt;/strong&gt; to install Jupyter Notebook like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install jupyter
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The next most popular distribution of Python is &lt;a href=&quot;https://www.anaconda.com/&quot;&gt;Anaconda&lt;/a&gt;. Anaconda has its own installer tool called &lt;strong&gt;conda&lt;/strong&gt; that you could use for installing a third-party package. However, Anaconda comes with many scientific libraries preinstalled, including the Jupyter Notebook, so you don&amp;rsquo;t actually need to do anything other than install Anaconda itself. &lt;/p&gt;
&lt;h3 id=&quot;starting-the-jupyter-notebook-server&quot;&gt;Starting the Jupyter Notebook Server&lt;/h3&gt;
&lt;p&gt;Now that you have Jupyter installed, let&amp;rsquo;s learn how to use it. To get started, all you need to do is open up your terminal application and go to a folder of your choice. I recommend using something like your Documents folder to start out with and create a subfolder there called &lt;em&gt;Notebooks&lt;/em&gt; or something else that is easy to remember. &lt;/p&gt;
&lt;p&gt;Then just go to that location in your terminal and 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; jupyter notebook
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will start up Jupyter and your default browser should start (or open a new tab) to the following URL: &lt;a href=&quot;http://localhost:8888/tree&quot;&gt;http://localhost:8888/tree&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Your browser should now look something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/01_initial_notebook_screen.cb2ea87d9679.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/01_initial_notebook_screen.cb2ea87d9679.png&quot; width=&quot;1986&quot; height=&quot;628&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/01_initial_notebook_screen.cb2ea87d9679.png&amp;amp;w=496&amp;amp;sig=2382a882d6ea1f610055b884cea855ee08dde276 496w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/01_initial_notebook_screen.cb2ea87d9679.png&amp;amp;w=993&amp;amp;sig=f33d7168d30bff9d06db1224ea32ce40ab760f2e 993w, https://files.realpython.com/media/01_initial_notebook_screen.cb2ea87d9679.png 1986w&quot; sizes=&quot;75vw&quot; alt=&quot;Jupyter Notebook Server&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Note that right now you are not actually running a Notebook, but instead you are just running the Notebook server. Let&amp;rsquo;s actually create a Notebook now!&lt;/p&gt;
&lt;h2 id=&quot;creating-a-notebook&quot;&gt;Creating a Notebook&lt;/h2&gt;
&lt;p&gt;Now that you know how to start a Notebook server, you should probably learn how to create an actually Notebook document.&lt;/p&gt;
&lt;p&gt;All you need to do is click on the &lt;em&gt;New&lt;/em&gt; button (upper right), and it will open up a list of choices. On my machine, I happen to have Python 2 and Python 3 installed, so I can create a Notebook that uses either of these. For simplicity&amp;rsquo;s sake, let&amp;rsquo;s choose Python 3.&lt;/p&gt;
&lt;p&gt;Your web page should now look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/02_new_notebook.015b2f84bb60.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/02_new_notebook.015b2f84bb60.png&quot; width=&quot;1576&quot; height=&quot;880&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/02_new_notebook.015b2f84bb60.png&amp;amp;w=394&amp;amp;sig=a63710942bf03f7dd36dab200e2828eaafdbeed6 394w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/02_new_notebook.015b2f84bb60.png&amp;amp;w=788&amp;amp;sig=1fc1d86464e6292bf742ab29c2c7594e10721a9c 788w, https://files.realpython.com/media/02_new_notebook.015b2f84bb60.png 1576w&quot; sizes=&quot;75vw&quot; alt=&quot;New Jupyter Notebook&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;naming&quot;&gt;Naming&lt;/h3&gt;
&lt;p&gt;You will notice that at the top of the page is the word &lt;em&gt;Untitled&lt;/em&gt;. This is the title for the page and the name of your Notebook. Since that isn&amp;rsquo;t a very descriptive name, let&amp;rsquo;s change it! &lt;/p&gt;
&lt;p&gt;Just move your mouse over the word &lt;em&gt;Untitled&lt;/em&gt; and click on the text. You should now see an in-browser dialog titled &lt;em&gt;Rename Notebook&lt;/em&gt;. Let&amp;rsquo;s rename this one to &lt;em&gt;Hello Jupyter&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/03_hello_jupyter.96024ca79ae6.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/03_hello_jupyter.96024ca79ae6.png&quot; width=&quot;1576&quot; height=&quot;682&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/03_hello_jupyter.96024ca79ae6.png&amp;amp;w=394&amp;amp;sig=dfafbf3062a1da8443c63f9d95d4475508b618ec 394w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/03_hello_jupyter.96024ca79ae6.png&amp;amp;w=788&amp;amp;sig=7e1165be2ebea0b9af359af1a03aff2937ed6c31 788w, https://files.realpython.com/media/03_hello_jupyter.96024ca79ae6.png 1576w&quot; sizes=&quot;75vw&quot; alt=&quot;Hello World in Jupyter&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;running-cells&quot;&gt;Running Cells&lt;/h3&gt;
&lt;p&gt;A Notebook&amp;rsquo;s cell defaults to using code whenever you first create one, and that cell uses the kernel that you chose when you started your Notebook.&lt;/p&gt;
&lt;p&gt;In this case, you started yours with Python 3 as your kernel, so that means you can write Python code in your code cells. Since your initial Notebook has only one empty cell in it, the Notebook can&amp;rsquo;t really do anything. &lt;/p&gt;
&lt;p&gt;Thus, to verify that everything is working as it should, you can add some Python code to the cell and try running its contents. &lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try adding the following code to that cell:&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;s1&quot;&gt;&amp;#39;Hello Jupyter!&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 a cell means that you will execute the cell&amp;rsquo;s contents. To execute a cell, you can just select the cell and click the &lt;em&gt;Run&lt;/em&gt; button that is in the row of buttons along the top. It&amp;rsquo;s towards the middle. If you prefer using your keyboard, you can just press &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-shift&quot;&gt;Shift&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-enter&quot;&gt;Enter&lt;/kbd&gt;&lt;/span&gt;. &lt;/p&gt;
&lt;p&gt;When I ran the code above, the output looked like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/04_cell_run.73e945f90bb1.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/04_cell_run.73e945f90bb1.png&quot; width=&quot;1164&quot; height=&quot;293&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/04_cell_run.73e945f90bb1.png&amp;amp;w=291&amp;amp;sig=f8862bbfb96abd60502b2c84ed250c49c72b475f 291w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/04_cell_run.73e945f90bb1.png&amp;amp;w=582&amp;amp;sig=7015d8c6dedcaf4de2e9192cd4398bb3f7ac5806 582w, https://files.realpython.com/media/04_cell_run.73e945f90bb1.png 1164w&quot; sizes=&quot;75vw&quot; alt=&quot;Running a Jupyter Notebook Cell&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you have multiple cells in your Notebook, and you run the cells in order, you can share your variables and imports across cells. This makes it easy to separate out your code into logical chunks without needing to reimport libraries or recreate variables or functions in every cell.&lt;/p&gt;
&lt;p&gt;When you run a cell, you will notice that there are some square braces next to the word &lt;em&gt;In&lt;/em&gt; to the left of the cell. The square braces will auto fill with a number that indicates the order that you ran the cells. For example, if you open a fresh Notebook and run the first cell at the top of the Notebook, the square braces will fill with the number &lt;em&gt;1&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-menus&quot;&gt;The Menus&lt;/h3&gt;
&lt;p&gt;The Jupyter Notebook has several menus that you can use to interact with your Notebook. The menu runs along the top of the Notebook just like menus do in other applications. Here is a list of the current menus:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;File&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Edit&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;View&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Insert&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Cell&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Kernel&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Widgets&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Help&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s go over the menus one by one. This article won&amp;rsquo;t go into detail for every single option in every menu, but it will focus on the items that are unique to the Notebook application.&lt;/p&gt;
&lt;p&gt;The first menu is the File menu. In it, you can create a new Notebook or open a preexisting one. This is also where you would go to rename a Notebook. I think the most interesting menu item is the &lt;em&gt;Save and Checkpoint&lt;/em&gt; option. This allows you to create checkpoints that you can roll back to if you need to.&lt;/p&gt;
&lt;p&gt;Next is the &lt;em&gt;Edit&lt;/em&gt; menu. Here you can cut, copy, and paste cells. This is also where you would go if you wanted to delete, split, or merge a cell. You can reorder cells here too.&lt;/p&gt;
&lt;p&gt;Note that some of the items in this menu are greyed out. The reason for this is that they do not apply to the currently selected cell. For example, a code cell cannot have an image inserted into it, but a Markdown cell can. If you see a greyed out menu item, try changing the cell&amp;rsquo;s type and see if the item becomes available to use.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;View&lt;/em&gt; menu is useful for toggling the visibility of the header and toolbar. You can also toggle &lt;em&gt;Line Numbers&lt;/em&gt; within cells on or off. This is also where you would go if you want to mess about with the cell&amp;rsquo;s toolbar.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Insert&lt;/em&gt; menu is just for inserting cells above or below the currently selected cell.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Cell&lt;/em&gt; menu allows you to run one cell, a group of cells, or all the cells. You can also go here to change a cell&amp;rsquo;s type, although I personally find the toolbar to be more intuitive for that.&lt;/p&gt;
&lt;p&gt;The other handy feature in this menu is the ability to clear a cell&amp;rsquo;s output. If you are planning to share your Notebook with others, you will probably want to clear the output first so that the next person can run the cells themselves.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Kernel&lt;/em&gt; cell is for working with the kernel that is running in the background. Here you can restart the kernel, reconnect to it, shut it down, or even change which kernel your Notebook is using.&lt;/p&gt;
&lt;p&gt;You probably won&amp;rsquo;t be working with the Kernel all that often, but there are times when you are debugging a Notebook that you will find you need to restart the Kernel. When that happens, this is where you would go.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Widgets&lt;/em&gt; menu is for saving and clearing widget state. Widgets are basically JavaScript widgets that you can add to your cells to make dynamic content using Python (or another Kernel).&lt;/p&gt;
&lt;p&gt;Finally you have the &lt;em&gt;Help&lt;/em&gt; menu, which is where you go to learn about the Notebook&amp;rsquo;s keyboard shortcuts, a user interface tour, and lots of reference material.&lt;/p&gt;
&lt;h3 id=&quot;starting-terminals-and-other-things&quot;&gt;Starting Terminals and Other Things&lt;/h3&gt;
&lt;p&gt;Jupyter Notebook also allows you to start more than just Notebooks. You can also create a text file, a folder, or a Terminal in your browser. Go back to the home page that opened when you first started the Jupyter server at &lt;code&gt;http://localhost:8888/tree&lt;/code&gt;. Go to the &lt;em&gt;New&lt;/em&gt; button and choose one of the other options.&lt;/p&gt;
&lt;p&gt;The Terminal is probably the most interesting of the bunch, as it is running your operating systems terminal in the browser. This allows you to run bash, Powershell, and so on in your browser and run any shell command that you might need to there.&lt;/p&gt;
&lt;h3 id=&quot;viewing-whats-running&quot;&gt;Viewing What&amp;rsquo;s Running&lt;/h3&gt;
&lt;p&gt;Also on the home page of your Jupyter server (&lt;code&gt;http://localhost:8888/tree&lt;/code&gt;) are two other tabs: &lt;em&gt;Running&lt;/em&gt; and &lt;em&gt;Clusters&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Running&lt;/em&gt; tab will tell you which Notebooks and Terminals you are currently running. This is useful for when you want to shut down your server but you need to make sure that you have saved all your data. Fortunately, Notebooks auto-save pretty frequently, so you rarely lose data. But it&amp;rsquo;s good to be able to see what&amp;rsquo;s running when you need to.&lt;/p&gt;
&lt;p&gt;The other nice thing about this tab is that you can go through your running applications and shut them down there.&lt;/p&gt;
&lt;h2 id=&quot;adding-rich-content&quot;&gt;Adding Rich Content&lt;/h2&gt;
&lt;p&gt;Jupyter Notebook supports adding rich content to its cells. In this section, you will get an overview of just some of the things you can do with your cells using Markup and Code.&lt;/p&gt;
&lt;h3 id=&quot;cell-types&quot;&gt;Cell Types&lt;/h3&gt;
&lt;p&gt;There are technically four cell types: Code, Markdown, Raw NBConvert, and Heading. &lt;/p&gt;
&lt;p&gt;The Heading cell type is no longer supported and will display a dialog that says as much. Instead, you are supposed to use Markdown for your Headings.&lt;/p&gt;
&lt;p&gt;The Raw NBConvert cell type is only intended for special use cases when using the &lt;code&gt;nbconvert&lt;/code&gt; command line tool. Basically it allows you to control the formatting in a very specific way when converting from a Notebook to another format.&lt;/p&gt;
&lt;p&gt;The primary cell types that you will use are the Code and Markdown cell types. You have already learned how code cells work, so let&amp;rsquo;s learn how to style your text with Markdown.&lt;/p&gt;
&lt;h3 id=&quot;styling-your-text&quot;&gt;Styling Your Text&lt;/h3&gt;
&lt;p&gt;Jupyter Notebook supports Markdown, which is a markup language that is a superset of HTML. This turotial will cover some of the basics of what you can do with Markdown.&lt;/p&gt;
&lt;p&gt;Set a new cell to Markdown and then add the following text to the cell: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/05_italic.e9e16a1040f5.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/05_italic.e9e16a1040f5.png&quot; width=&quot;1162&quot; height=&quot;267&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/05_italic.e9e16a1040f5.png&amp;amp;w=290&amp;amp;sig=283330fdd4dcb30ed0955e01d1de34b74bc76291 290w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/05_italic.e9e16a1040f5.png&amp;amp;w=581&amp;amp;sig=a6c503abf72ea9e7076171f5afe556f89db0bb2e 581w, https://files.realpython.com/media/05_italic.e9e16a1040f5.png 1162w&quot; sizes=&quot;75vw&quot; alt=&quot;Italicized Text in Jupyter Notebook&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When you run the cell, the output should look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/06_italic_ran.63ba240b2492.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/06_italic_ran.63ba240b2492.png&quot; width=&quot;1153&quot; height=&quot;260&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/06_italic_ran.63ba240b2492.png&amp;amp;w=288&amp;amp;sig=5fc1e0c6c0206b32b40ebc7f2845393570cf45b1 288w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/06_italic_ran.63ba240b2492.png&amp;amp;w=576&amp;amp;sig=b2d55cebe710bd28764d64f84dc4fad520d3869b 576w, https://files.realpython.com/media/06_italic_ran.63ba240b2492.png 1153w&quot; sizes=&quot;75vw&quot; alt=&quot;Italicized Text Output in Jupyter Notebook&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you would prefer to bold your text, use a double underscore or double asterisk.&lt;/p&gt;
&lt;h3 id=&quot;headers&quot;&gt;Headers&lt;/h3&gt;
&lt;p&gt;Creating headers in Markdown is also quite simple. You just have to use the humble pound sign. The more pound signs you use, the smaller the header. Jupyter Notebook even kind of previews it for you:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/07_headers.dc5aa8999b03.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/07_headers.dc5aa8999b03.png&quot; width=&quot;1160&quot; height=&quot;306&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/07_headers.dc5aa8999b03.png&amp;amp;w=290&amp;amp;sig=ba83b18e74b67ef53ff6d94df0e2bf49663bd43e 290w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/07_headers.dc5aa8999b03.png&amp;amp;w=580&amp;amp;sig=9c7e94adda1511c2dfe3e8ecfcd9eaab22cbab36 580w, https://files.realpython.com/media/07_headers.dc5aa8999b03.png 1160w&quot; sizes=&quot;75vw&quot; alt=&quot;Header Markdown in Jupyter Notebooks&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then when you run the cell, you will end up with a nicely formatted header:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/08_headers.9cfb98853821.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/08_headers.9cfb98853821.png&quot; width=&quot;1163&quot; height=&quot;382&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/08_headers.9cfb98853821.png&amp;amp;w=290&amp;amp;sig=3096f77fa38200adb2fcc52d0e85ac4104a95e27 290w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/08_headers.9cfb98853821.png&amp;amp;w=581&amp;amp;sig=dba2bc93f6e9883f2e8784ff1cc0d3926c4a40b3 581w, https://files.realpython.com/media/08_headers.9cfb98853821.png 1163w&quot; sizes=&quot;75vw&quot; alt=&quot;Headers in Jupyter Notebooks&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;creating-lists&quot;&gt;Creating Lists&lt;/h3&gt;
&lt;p&gt;You can create a list (bullet points) by using dashes, plus signs, or asterisks. Here is an example:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/09_list.bb44656fd178.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/09_list.bb44656fd178.png&quot; width=&quot;1167&quot; height=&quot;308&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/09_list.bb44656fd178.png&amp;amp;w=291&amp;amp;sig=5d69cfe7130e025aac354b0781c7429592494da1 291w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/09_list.bb44656fd178.png&amp;amp;w=583&amp;amp;sig=f7e2d11fa810be3e5d54b3163ebe41126d4bcaee 583w, https://files.realpython.com/media/09_list.bb44656fd178.png 1167w&quot; sizes=&quot;75vw&quot; alt=&quot;Markdown Lists in Jupyter Notebooks&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;code-and-syntax-highlighting&quot;&gt;Code and Syntax Highlighting&lt;/h3&gt;
&lt;p&gt;If you want to insert a code example that you don&amp;rsquo;t want your end user to actually run, you can use Markdown to insert it. For inline code highlighting, just surround the code with backticks. If you want to insert a block of code, you can use triple backticks and also specify the programming language:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/syntax_highlighting_markdown.7e65e64cbf42.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/syntax_highlighting_markdown.7e65e64cbf42.png&quot; width=&quot;1976&quot; height=&quot;794&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/syntax_highlighting_markdown.7e65e64cbf42.png&amp;amp;w=494&amp;amp;sig=345da5bc75e0752710bee548fbc99abfc890c2f1 494w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/syntax_highlighting_markdown.7e65e64cbf42.png&amp;amp;w=988&amp;amp;sig=41e5556bba22a3300062eecd2d44ff4705931435 988w, https://files.realpython.com/media/syntax_highlighting_markdown.7e65e64cbf42.png 1976w&quot; sizes=&quot;75vw&quot; alt=&quot;Highlighting Code Syntax&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;exporting-notebooks&quot;&gt;Exporting Notebooks&lt;/h2&gt;
&lt;p&gt;When you are working with Jupyter Notebooks, you will find that you need to share your results with non-technical people. When that happens, you can use the &lt;code&gt;nbconvert&lt;/code&gt; tool which comes with Jupyter Notebook to convert or export your Notebook into one of the following formats:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;LaTeX&lt;/li&gt;
&lt;li&gt;PDF&lt;/li&gt;
&lt;li&gt;RevealJS&lt;/li&gt;
&lt;li&gt;Markdown&lt;/li&gt;
&lt;li&gt;ReStructured Text&lt;/li&gt;
&lt;li&gt;Executable script&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;nbconvert&lt;/code&gt; tool uses Jinja templates under the covers to convert your Notebook files (&lt;code&gt;.ipynb&lt;/code&gt;) into these other formats.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://jinja.pocoo.org/&quot;&gt;Jinja&lt;/a&gt; is a template engine that was made for Python. Also note that &lt;code&gt;nbconvert&lt;/code&gt; also depends on &lt;a href=&quot;https://pandoc.org/&quot;&gt;Pandoc&lt;/a&gt; and TeX to be able to export to all the formats above. If you don&amp;rsquo;t have one or more of these, some of the export types may not work. For more information, you should check out the &lt;a href=&quot;https://nbconvert.readthedocs.io/en/latest/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;how-to-use-nbconvert&quot;&gt;How to Use &lt;code&gt;nbconvert&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;nbconvert&lt;/code&gt; command does not take very many parameters, which makes learning how to use it easier. Open up a terminal and navigate to the folder that contains the Notebook you wish to convert. The basic conversion command looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; jupyter nbconvert &amp;lt;input 
&lt;span class=&quot;go&quot;&gt;notebook&amp;gt; --to &amp;lt;output format&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;example-usage&quot;&gt;Example Usage&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s pretend that you have a Notebook named &lt;code&gt;py_examples.ipynb&lt;/code&gt; and you want to convert it to PDF. Here is the command you would use to do that:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; jupyter nbconvert py_examples.ipynb --to pdf
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you run this command, you should see some output that tells you about the conversion process. &lt;code&gt;nbconvert&lt;/code&gt; will display warnings and errors if there are any. Assuming everything goes according to plan, you will now have a &lt;code&gt;py_examples.pdf&lt;/code&gt; file in your folder.&lt;/p&gt;
&lt;p&gt;The conversion process for the other file types is quite similar. You just have to tell &lt;code&gt;nbconvert&lt;/code&gt; what type to convert to (PDF, Markdown, HTML, and so on).&lt;/p&gt;
&lt;h3 id=&quot;use-the-menu&quot;&gt;Use the Menu&lt;/h3&gt;
&lt;p&gt;You can also export your currently running Notebook by going to the &lt;em&gt;File&lt;/em&gt; menu and choosing the &lt;em&gt;Download as&lt;/em&gt; option.&lt;/p&gt;
&lt;p&gt;This option allows you to download in all the formats that &lt;code&gt;nbconvert&lt;/code&gt; supports. The benefit of using the menu is that you don&amp;rsquo;t need to learn the &lt;code&gt;nbconvert&lt;/code&gt; at all if you don&amp;rsquo;t want to. However I recommend doing so as you can use &lt;code&gt;nbconvert&lt;/code&gt; to export multiple Notebooks at once, which is something that the menu does not support.&lt;/p&gt;
&lt;h2 id=&quot;notebook-extensions&quot;&gt;Notebook Extensions&lt;/h2&gt;
&lt;p&gt;While Jupyter Notebooks have lots of functionality built in, you can add new functionality through extensions. Jupyter actually supports four types of extensions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kernel&lt;/li&gt;
&lt;li&gt;IPython kernel&lt;/li&gt;
&lt;li&gt;Notebook &lt;/li&gt;
&lt;li&gt;Notebook server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This tutorial will focus on Notebook extensions.&lt;/p&gt;
&lt;h3 id=&quot;what-are-extensions&quot;&gt;What Are Extensions?&lt;/h3&gt;
&lt;p&gt;A Notebook extension (&lt;code&gt;nbextension&lt;/code&gt;) is a JavaScript module that you load in most of the views in the Notebook&amp;rsquo;s frontend. If you are handy with JavaScript, you can even write your own extension. An extension can access the page&amp;rsquo;s DOM and the Jupyter JavaScript API.&lt;/p&gt;
&lt;h3 id=&quot;where-do-i-get-extensions&quot;&gt;Where Do I Get Extensions?&lt;/h3&gt;
&lt;p&gt;You can use Google or search for Jupyter Notebook extensions. There are actually quite a few out there. One of the most popular extension sets is called &lt;strong&gt;jupyter_contrib_nbextensions&lt;/strong&gt;, which you can get from &lt;a href=&quot;https://github.com/ipython-contrib/jupyter_contrib_nbextensions&quot;&gt;GitHub&lt;/a&gt;. This is actually a collection of extensions that is provided by the Jupyter community and installed with &lt;code&gt;pip&lt;/code&gt;. &lt;/p&gt;
&lt;h3 id=&quot;how-do-i-install-them&quot;&gt;How Do I Install Them?&lt;/h3&gt;
&lt;p&gt;Most Jupyter Notebook extensions can be installed using Python&amp;rsquo;s &lt;code&gt;pip&lt;/code&gt; tool. If you find an extension that can&amp;rsquo;t be installed with &lt;code&gt;pip&lt;/code&gt;, then you will likely have to use the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; jupyter nbextension install EXTENSION_NAME
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This only installs the extension but does not make it active. You will need to enable an extension after installing it by running the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; jupyter nbextension &lt;span class=&quot;nb&quot;&gt;enable&lt;/span&gt; EXTENSION_NAME
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You may need to restart your Jupyter Notebook kernel to see the extension. &lt;/p&gt;
&lt;p&gt;There is a nice meta extension called &lt;strong&gt;Jupyter NbExtensions Configurator&lt;/strong&gt; that is worth getting for managing other extensions. It allows you to enable and disable your extensions from within the Jupyter Notebook&amp;rsquo;s user interface and also shows all the currently installed extensions.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The Jupyter Notebook is quite useful not only for learning and teaching a programming language such as Python but also for sharing your data. &lt;/p&gt;
&lt;p&gt;You can turn your Notebook into a slideshow or share it online with GitHub. If you want to share a Notebook without requiring your users to install anything, you can use &lt;a href=&quot;https://mybinder.org/&quot;&gt;binder&lt;/a&gt; for that.&lt;/p&gt;
&lt;p&gt;Google and Microsoft both have their own version of the Notebook that you can use to create and share your Notebooks at &lt;a href=&quot;https://colab.research.google.com&quot;&gt;Google Colaboratory&lt;/a&gt; and &lt;a href=&quot;https://notebooks.azure.com/&quot;&gt;Microsoft Azure Notebooks&lt;/a&gt; respectively. You can browse really interesting Notebooks there as well.&lt;/p&gt;
&lt;p&gt;Project Jupyter recently launched their latest product, &lt;a href=&quot;https://jupyterlab.readthedocs.io/en/stable/&quot;&gt;JupyterLab&lt;/a&gt;. JupyterLab incorporates Jupyter Notebook into an Integrated Development type Editor that you run in your browser. You can kind of think of JupyterLab as an advanced version of Jupyter Notebook. JupyterLab allows you to run terminals, text editors and code consoles in your browser in addition to Notebooks.&lt;/p&gt;
&lt;p&gt;As always, it is best to try out a new piece of software yourself to see if it suits you and is worth using. I encourage you to give Jupyter Notebook or JupyterLab a spin and see what you think!&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;
&lt;p&gt;If you want to learn more about Jupyter Notebook, you can take a moment and read their excellent &lt;a href=&quot;https://jupyter-notebook.readthedocs.io/en/stable/&quot;&gt;documentation&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;For more information on Integrated Development Environments, you might want to check out the following articles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-ides-code-editors-guide/&quot;&gt;Python IDEs and Code Editors (Guide)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-thonny/&quot;&gt;Thonny: The Beginner-Friendly Python Editor&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’s Requests Library (Guide)</title>
      <id>https://realpython.com/python-requests/</id>
      <link href="https://realpython.com/python-requests/"/>
      <updated>2019-01-23T06:00:00+00:00</updated>
      <summary>In this tutorial on Python&#39;s &quot;requests&quot; library, you&#39;ll see some of the most useful features that requests has to offer as well as how to customize and optimize those features. You&#39;ll learn how to use requests efficiently and stop requests to external services from slowing down your application.</summary>
      <content type="html">
        &lt;p&gt;The &lt;a href=&quot;http://docs.python-requests.org/en/master/&quot;&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; library is the de facto standard for making HTTP requests in Python. It abstracts the complexities of making requests behind a beautiful, simple API so that you can focus on interacting with services and consuming data in your application.&lt;/p&gt;
&lt;p&gt;Throughout this article, you&amp;rsquo;ll see some of the most useful features that &lt;code&gt;requests&lt;/code&gt; has to offer as well as how to customize and optimize those features for different situations you may come across. You&amp;rsquo;ll also learn how to use &lt;code&gt;requests&lt;/code&gt; in an efficient way as well as how to prevent requests to external services from slowing down your application.&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;strong&gt;Make requests&lt;/strong&gt; using the most common HTTP methods&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customize&lt;/strong&gt; your requests&amp;rsquo; headers and data, using the query string and message body&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inspect&lt;/strong&gt; data from your requests and responses&lt;/li&gt;
&lt;li&gt;Make &lt;strong&gt;authenticated&lt;/strong&gt; requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configure&lt;/strong&gt; your requests to help prevent your application from backing up or slowing down&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Though I&amp;rsquo;ve tried to include as much information as you need to understand the features and examples included in this article, I do assume a &lt;em&gt;very&lt;/em&gt; &lt;a href=&quot;https://www.w3schools.com/tags/ref_httpmethods.asp&quot;&gt;basic general knowledge of HTTP&lt;/a&gt;. That said, you still may be able to follow along fine anyway.&lt;/p&gt;
&lt;p&gt;Now that that is out of the way, let&amp;rsquo;s dive in and see how you can use &lt;code&gt;requests&lt;/code&gt; in your application!&lt;/p&gt;
&lt;h2 id=&quot;getting-started-with-requests&quot;&gt;Getting Started With &lt;code&gt;requests&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s begin by installing the &lt;code&gt;requests&lt;/code&gt; library. To do so, 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; pip install requests
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you prefer to use &lt;a href=&quot;https://realpython.com/pipenv-guide/&quot;&gt;Pipenv&lt;/a&gt; for managing Python packages, you can run the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pipenv install requests
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once &lt;code&gt;requests&lt;/code&gt; is installed, you can use it in your application. Importing &lt;code&gt;requests&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;requests&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that you&amp;rsquo;re all set up, it&amp;rsquo;s time to begin your journey through &lt;code&gt;requests&lt;/code&gt;. Your first goal will be learning how to make a &lt;code&gt;GET&lt;/code&gt; request.&lt;/p&gt;
&lt;h2 id=&quot;the-get-request&quot;&gt;The GET Request&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods&quot;&gt;HTTP methods&lt;/a&gt; such as &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt;, determine which action you&amp;rsquo;re trying to perform when making an HTTP request. Besides &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt;, there are several other common methods that you&amp;rsquo;ll use later in this tutorial.&lt;/p&gt;
&lt;p&gt;One of the most common HTTP methods is &lt;code&gt;GET&lt;/code&gt;. The &lt;code&gt;GET&lt;/code&gt; method indicates that you&amp;rsquo;re trying to get or retrieve data from a specified resource. To make a &lt;code&gt;GET&lt;/code&gt; request, invoke &lt;code&gt;requests.get()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To test this out, you can make a &lt;code&gt;GET&lt;/code&gt; request to GitHub&amp;rsquo;s &lt;a href=&quot;https://developer.github.com/v3/#root-endpoint&quot;&gt;Root REST API&lt;/a&gt; by calling &lt;code&gt;get()&lt;/code&gt; with the following URL:&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Congratulations! You&amp;rsquo;ve made your first request. Let&amp;rsquo;s dive a little deeper into the response of that request.&lt;/p&gt;
&lt;h2 id=&quot;the-response&quot;&gt;The Response&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;Response&lt;/code&gt; is a powerful object for inspecting the results of the request. Let&amp;rsquo;s make that same request again, but this time store the return value in a variable so that you can get a closer look at its attributes and behaviors:&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;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&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 this example, you&amp;rsquo;ve captured the return value of &lt;code&gt;get()&lt;/code&gt;, which is an instance of &lt;code&gt;Response&lt;/code&gt;, and stored it in a variable called &lt;code&gt;response&lt;/code&gt;. You can now use &lt;code&gt;response&lt;/code&gt; to see a lot of information about the results of your &lt;code&gt;GET&lt;/code&gt; request.&lt;/p&gt;
&lt;h3 id=&quot;status-codes&quot;&gt;Status Codes&lt;/h3&gt;
&lt;p&gt;The first bit of information that you can gather from &lt;code&gt;Response&lt;/code&gt; is the status code. A status code informs you of the status of the request.&lt;/p&gt;
&lt;p&gt;For example, a &lt;code&gt;200 OK&lt;/code&gt; status means that your request was successful, whereas a &lt;code&gt;404 NOT FOUND&lt;/code&gt; status means that the resource you were looking for was not found. There are &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_HTTP_status_codes&quot;&gt;many other possible status codes&lt;/a&gt; as well to give you specific insights into what happened with your request.&lt;/p&gt;
&lt;p&gt;By accessing &lt;code&gt;.status_code&lt;/code&gt;, you can see the status code that the server returned:&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;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;go&quot;&gt;200&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.status_code&lt;/code&gt; returned a &lt;code&gt;200&lt;/code&gt;, which means your request was successful and the server responded with the data you were requesting.&lt;/p&gt;
&lt;p&gt;Sometimes, you might want to use this information to make decisions in your 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;k&quot;&gt;if&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;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;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;Success!&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;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;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;404&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;Not Found.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With this logic, if the server returns a &lt;code&gt;200&lt;/code&gt; status code, your program will print &lt;code&gt;Success!&lt;/code&gt;. If the result is a &lt;code&gt;404&lt;/code&gt;, your program will print &lt;code&gt;Not Found&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;requests&lt;/code&gt; goes one step further in simplifying this process for you. If you use a &lt;code&gt;Response&lt;/code&gt; instance in a conditional expression, it will evaluate to &lt;code&gt;True&lt;/code&gt; if the status code was between &lt;code&gt;200&lt;/code&gt; and &lt;code&gt;400&lt;/code&gt;, and &lt;code&gt;False&lt;/code&gt; otherwise.&lt;/p&gt;
&lt;p&gt;Therefore, you can simplify the last example by rewriting the &lt;code&gt;if&lt;/code&gt; statement:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;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;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;An error has occurred.&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;Technical Detail:&lt;/strong&gt; This &lt;a href=&quot;https://docs.python.org/3/library/stdtypes.html#truth-value-testing&quot;&gt;Truth Value Test&lt;/a&gt; is made possible because &lt;a href=&quot;https://realpython.com/operator-function-overloading/#making-your-objects-truthy-or-falsey-using-bool&quot;&gt;&lt;code&gt;__bool__()&lt;/code&gt; is an overloaded method&lt;/a&gt; on &lt;code&gt;Response&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This means that the default behavior of &lt;code&gt;Response&lt;/code&gt; has been redefined to take the status code into account when determining the truth value of the object.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Keep in mind that this method is &lt;em&gt;not&lt;/em&gt; verifying that the status code is equal to &lt;code&gt;200&lt;/code&gt;. The reason for this is that other status codes within the &lt;code&gt;200&lt;/code&gt; to &lt;code&gt;400&lt;/code&gt; range, such as &lt;code&gt;204 NO CONTENT&lt;/code&gt; and &lt;code&gt;304 NOT MODIFIED&lt;/code&gt;, are also considered successful in the sense that they provide some workable response.&lt;/p&gt;
&lt;p&gt;For example, the &lt;code&gt;204&lt;/code&gt; tells you that the response was successful, but there&amp;rsquo;s no content to return in the message body.&lt;/p&gt;
&lt;p&gt;So, make sure you use this convenient shorthand only if you want to know if the request was generally successful and then, if necessary, handle the response appropriately based on the status code.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say you don&amp;rsquo;t want to check the response&amp;rsquo;s status code in an &lt;code&gt;if&lt;/code&gt; statement. Instead, you want to raise an exception if the request was unsuccessful. You can do this using &lt;code&gt;.raise_for_status()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTPError&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/invalid&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;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# If the response was successful, no Exception will be raised&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;raise_for_status&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;HTTPError&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;HTTP error occurred: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{http_err}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Python 3.6&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;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Other error occurred: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{err}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Python 3.6&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;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;Success!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you invoke &lt;code&gt;.raise_for_status()&lt;/code&gt;, an &lt;code&gt;HTTPError&lt;/code&gt; will be raised for certain status codes. If the status code indicates a successful request, the program will proceed without that exception being raised.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt; If you&amp;rsquo;re not familiar with Python 3.6&amp;rsquo;s &lt;a href=&quot;https://realpython.com/python-f-strings/&quot;&gt;f-strings&lt;/a&gt;, I encourage you to take advantage of them as they are a great way to simplify your formatted strings.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now, you know a lot about how to deal with the status code of the response you got back from the server. However, when you make a &lt;code&gt;GET&lt;/code&gt; request, you rarely only care about the status code of the response. Usually, you want to see more. Next, you&amp;rsquo;ll see how to view the actual data that the server sent back in the body of the response.&lt;/p&gt;
&lt;h3 id=&quot;content&quot;&gt;Content&lt;/h3&gt;
&lt;p&gt;The response of a &lt;code&gt;GET&lt;/code&gt; request often has some valuable information, known as a payload, in the message body. Using the attributes and methods of &lt;code&gt;Response&lt;/code&gt;, you can view the payload in a variety of different formats.&lt;/p&gt;
&lt;p&gt;To see the response&amp;rsquo;s content in &lt;a href=&quot;https://realpython.com/python-strings/&quot;&gt;&lt;code&gt;bytes&lt;/code&gt;&lt;/a&gt;, you use &lt;code&gt;.content&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;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&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;response&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;go&quot;&gt;b&amp;#39;{&amp;quot;current_user_url&amp;quot;:&amp;quot;https://api.github.com/user&amp;quot;,&amp;quot;current_user_authorizations_html_url&amp;quot;:&amp;quot;https://github.com/settings/connections/applications{/client_id}&amp;quot;,&amp;quot;authorizations_url&amp;quot;:&amp;quot;https://api.github.com/authorizations&amp;quot;,&amp;quot;code_search_url&amp;quot;:&amp;quot;https://api.github.com/search/code?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;commit_search_url&amp;quot;:&amp;quot;https://api.github.com/search/commits?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;emails_url&amp;quot;:&amp;quot;https://api.github.com/user/emails&amp;quot;,&amp;quot;emojis_url&amp;quot;:&amp;quot;https://api.github.com/emojis&amp;quot;,&amp;quot;events_url&amp;quot;:&amp;quot;https://api.github.com/events&amp;quot;,&amp;quot;feeds_url&amp;quot;:&amp;quot;https://api.github.com/feeds&amp;quot;,&amp;quot;followers_url&amp;quot;:&amp;quot;https://api.github.com/user/followers&amp;quot;,&amp;quot;following_url&amp;quot;:&amp;quot;https://api.github.com/user/following{/target}&amp;quot;,&amp;quot;gists_url&amp;quot;:&amp;quot;https://api.github.com/gists{/gist_id}&amp;quot;,&amp;quot;hub_url&amp;quot;:&amp;quot;https://api.github.com/hub&amp;quot;,&amp;quot;issue_search_url&amp;quot;:&amp;quot;https://api.github.com/search/issues?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;issues_url&amp;quot;:&amp;quot;https://api.github.com/issues&amp;quot;,&amp;quot;keys_url&amp;quot;:&amp;quot;https://api.github.com/user/keys&amp;quot;,&amp;quot;notifications_url&amp;quot;:&amp;quot;https://api.github.com/notifications&amp;quot;,&amp;quot;organization_repositories_url&amp;quot;:&amp;quot;https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}&amp;quot;,&amp;quot;organization_url&amp;quot;:&amp;quot;https://api.github.com/orgs/{org}&amp;quot;,&amp;quot;public_gists_url&amp;quot;:&amp;quot;https://api.github.com/gists/public&amp;quot;,&amp;quot;rate_limit_url&amp;quot;:&amp;quot;https://api.github.com/rate_limit&amp;quot;,&amp;quot;repository_url&amp;quot;:&amp;quot;https://api.github.com/repos/{owner}/{repo}&amp;quot;,&amp;quot;repository_search_url&amp;quot;:&amp;quot;https://api.github.com/search/repositories?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;current_user_repositories_url&amp;quot;:&amp;quot;https://api.github.com/user/repos{?type,page,per_page,sort}&amp;quot;,&amp;quot;starred_url&amp;quot;:&amp;quot;https://api.github.com/user/starred{/owner}{/repo}&amp;quot;,&amp;quot;starred_gists_url&amp;quot;:&amp;quot;https://api.github.com/gists/starred&amp;quot;,&amp;quot;team_url&amp;quot;:&amp;quot;https://api.github.com/teams&amp;quot;,&amp;quot;user_url&amp;quot;:&amp;quot;https://api.github.com/users/{user}&amp;quot;,&amp;quot;user_organizations_url&amp;quot;:&amp;quot;https://api.github.com/user/orgs&amp;quot;,&amp;quot;user_repositories_url&amp;quot;:&amp;quot;https://api.github.com/users/{user}/repos{?type,page,per_page,sort}&amp;quot;,&amp;quot;user_search_url&amp;quot;:&amp;quot;https://api.github.com/search/users?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;}&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While &lt;code&gt;.content&lt;/code&gt; gives you access to the raw bytes of the response payload, you will often want to convert them into a &lt;a href=&quot;https://realpython.com/python-data-types/&quot;&gt;string&lt;/a&gt; using a character encoding such as &lt;a href=&quot;https://en.wikipedia.org/wiki/UTF-8&quot;&gt;UTF-8&lt;/a&gt;. &lt;code&gt;response&lt;/code&gt; will do that for you when you access &lt;code&gt;.text&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;response&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;go&quot;&gt;&amp;#39;{&amp;quot;current_user_url&amp;quot;:&amp;quot;https://api.github.com/user&amp;quot;,&amp;quot;current_user_authorizations_html_url&amp;quot;:&amp;quot;https://github.com/settings/connections/applications{/client_id}&amp;quot;,&amp;quot;authorizations_url&amp;quot;:&amp;quot;https://api.github.com/authorizations&amp;quot;,&amp;quot;code_search_url&amp;quot;:&amp;quot;https://api.github.com/search/code?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;commit_search_url&amp;quot;:&amp;quot;https://api.github.com/search/commits?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;emails_url&amp;quot;:&amp;quot;https://api.github.com/user/emails&amp;quot;,&amp;quot;emojis_url&amp;quot;:&amp;quot;https://api.github.com/emojis&amp;quot;,&amp;quot;events_url&amp;quot;:&amp;quot;https://api.github.com/events&amp;quot;,&amp;quot;feeds_url&amp;quot;:&amp;quot;https://api.github.com/feeds&amp;quot;,&amp;quot;followers_url&amp;quot;:&amp;quot;https://api.github.com/user/followers&amp;quot;,&amp;quot;following_url&amp;quot;:&amp;quot;https://api.github.com/user/following{/target}&amp;quot;,&amp;quot;gists_url&amp;quot;:&amp;quot;https://api.github.com/gists{/gist_id}&amp;quot;,&amp;quot;hub_url&amp;quot;:&amp;quot;https://api.github.com/hub&amp;quot;,&amp;quot;issue_search_url&amp;quot;:&amp;quot;https://api.github.com/search/issues?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;issues_url&amp;quot;:&amp;quot;https://api.github.com/issues&amp;quot;,&amp;quot;keys_url&amp;quot;:&amp;quot;https://api.github.com/user/keys&amp;quot;,&amp;quot;notifications_url&amp;quot;:&amp;quot;https://api.github.com/notifications&amp;quot;,&amp;quot;organization_repositories_url&amp;quot;:&amp;quot;https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}&amp;quot;,&amp;quot;organization_url&amp;quot;:&amp;quot;https://api.github.com/orgs/{org}&amp;quot;,&amp;quot;public_gists_url&amp;quot;:&amp;quot;https://api.github.com/gists/public&amp;quot;,&amp;quot;rate_limit_url&amp;quot;:&amp;quot;https://api.github.com/rate_limit&amp;quot;,&amp;quot;repository_url&amp;quot;:&amp;quot;https://api.github.com/repos/{owner}/{repo}&amp;quot;,&amp;quot;repository_search_url&amp;quot;:&amp;quot;https://api.github.com/search/repositories?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;current_user_repositories_url&amp;quot;:&amp;quot;https://api.github.com/user/repos{?type,page,per_page,sort}&amp;quot;,&amp;quot;starred_url&amp;quot;:&amp;quot;https://api.github.com/user/starred{/owner}{/repo}&amp;quot;,&amp;quot;starred_gists_url&amp;quot;:&amp;quot;https://api.github.com/gists/starred&amp;quot;,&amp;quot;team_url&amp;quot;:&amp;quot;https://api.github.com/teams&amp;quot;,&amp;quot;user_url&amp;quot;:&amp;quot;https://api.github.com/users/{user}&amp;quot;,&amp;quot;user_organizations_url&amp;quot;:&amp;quot;https://api.github.com/user/orgs&amp;quot;,&amp;quot;user_repositories_url&amp;quot;:&amp;quot;https://api.github.com/users/{user}/repos{?type,page,per_page,sort}&amp;quot;,&amp;quot;user_search_url&amp;quot;:&amp;quot;https://api.github.com/search/users?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;}&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because the decoding of &lt;code&gt;bytes&lt;/code&gt; to a &lt;code&gt;str&lt;/code&gt; requires an encoding scheme, &lt;code&gt;requests&lt;/code&gt; will try to guess the &lt;a href=&quot;https://docs.python.org/3/howto/unicode.html#encodings&quot;&gt;encoding&lt;/a&gt; based on the response&amp;rsquo;s &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers&quot;&gt;headers&lt;/a&gt; if you do not specify one. You can provide an explicit encoding by setting &lt;code&gt;.encoding&lt;/code&gt; before accessing &lt;code&gt;.text&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;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Optional: requests infers this internally&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;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;text&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;{&amp;quot;current_user_url&amp;quot;:&amp;quot;https://api.github.com/user&amp;quot;,&amp;quot;current_user_authorizations_html_url&amp;quot;:&amp;quot;https://github.com/settings/connections/applications{/client_id}&amp;quot;,&amp;quot;authorizations_url&amp;quot;:&amp;quot;https://api.github.com/authorizations&amp;quot;,&amp;quot;code_search_url&amp;quot;:&amp;quot;https://api.github.com/search/code?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;commit_search_url&amp;quot;:&amp;quot;https://api.github.com/search/commits?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;emails_url&amp;quot;:&amp;quot;https://api.github.com/user/emails&amp;quot;,&amp;quot;emojis_url&amp;quot;:&amp;quot;https://api.github.com/emojis&amp;quot;,&amp;quot;events_url&amp;quot;:&amp;quot;https://api.github.com/events&amp;quot;,&amp;quot;feeds_url&amp;quot;:&amp;quot;https://api.github.com/feeds&amp;quot;,&amp;quot;followers_url&amp;quot;:&amp;quot;https://api.github.com/user/followers&amp;quot;,&amp;quot;following_url&amp;quot;:&amp;quot;https://api.github.com/user/following{/target}&amp;quot;,&amp;quot;gists_url&amp;quot;:&amp;quot;https://api.github.com/gists{/gist_id}&amp;quot;,&amp;quot;hub_url&amp;quot;:&amp;quot;https://api.github.com/hub&amp;quot;,&amp;quot;issue_search_url&amp;quot;:&amp;quot;https://api.github.com/search/issues?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;issues_url&amp;quot;:&amp;quot;https://api.github.com/issues&amp;quot;,&amp;quot;keys_url&amp;quot;:&amp;quot;https://api.github.com/user/keys&amp;quot;,&amp;quot;notifications_url&amp;quot;:&amp;quot;https://api.github.com/notifications&amp;quot;,&amp;quot;organization_repositories_url&amp;quot;:&amp;quot;https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}&amp;quot;,&amp;quot;organization_url&amp;quot;:&amp;quot;https://api.github.com/orgs/{org}&amp;quot;,&amp;quot;public_gists_url&amp;quot;:&amp;quot;https://api.github.com/gists/public&amp;quot;,&amp;quot;rate_limit_url&amp;quot;:&amp;quot;https://api.github.com/rate_limit&amp;quot;,&amp;quot;repository_url&amp;quot;:&amp;quot;https://api.github.com/repos/{owner}/{repo}&amp;quot;,&amp;quot;repository_search_url&amp;quot;:&amp;quot;https://api.github.com/search/repositories?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;,&amp;quot;current_user_repositories_url&amp;quot;:&amp;quot;https://api.github.com/user/repos{?type,page,per_page,sort}&amp;quot;,&amp;quot;starred_url&amp;quot;:&amp;quot;https://api.github.com/user/starred{/owner}{/repo}&amp;quot;,&amp;quot;starred_gists_url&amp;quot;:&amp;quot;https://api.github.com/gists/starred&amp;quot;,&amp;quot;team_url&amp;quot;:&amp;quot;https://api.github.com/teams&amp;quot;,&amp;quot;user_url&amp;quot;:&amp;quot;https://api.github.com/users/{user}&amp;quot;,&amp;quot;user_organizations_url&amp;quot;:&amp;quot;https://api.github.com/user/orgs&amp;quot;,&amp;quot;user_repositories_url&amp;quot;:&amp;quot;https://api.github.com/users/{user}/repos{?type,page,per_page,sort}&amp;quot;,&amp;quot;user_search_url&amp;quot;:&amp;quot;https://api.github.com/search/users?q={query}{&amp;amp;page,per_page,sort,order}&amp;quot;}&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you take a look at the response, you&amp;rsquo;ll see that it is actually serialized JSON content. To get a dictionary, you could take the &lt;code&gt;str&lt;/code&gt; you retrieved from &lt;code&gt;.text&lt;/code&gt; and deserialize it using &lt;a href=&quot;https://realpython.com/python-json/#deserializing-json&quot;&gt;&lt;code&gt;json.loads()&lt;/code&gt;&lt;/a&gt;. However, a simpler way to accomplish this task is to use &lt;code&gt;.json()&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;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;current_user_url&amp;#39;: &amp;#39;https://api.github.com/user&amp;#39;, &amp;#39;current_user_authorizations_html_url&amp;#39;: &amp;#39;https://github.com/settings/connections/applications{/client_id}&amp;#39;, &amp;#39;authorizations_url&amp;#39;: &amp;#39;https://api.github.com/authorizations&amp;#39;, &amp;#39;code_search_url&amp;#39;: &amp;#39;https://api.github.com/search/code?q={query}{&amp;amp;page,per_page,sort,order}&amp;#39;, &amp;#39;commit_search_url&amp;#39;: &amp;#39;https://api.github.com/search/commits?q={query}{&amp;amp;page,per_page,sort,order}&amp;#39;, &amp;#39;emails_url&amp;#39;: &amp;#39;https://api.github.com/user/emails&amp;#39;, &amp;#39;emojis_url&amp;#39;: &amp;#39;https://api.github.com/emojis&amp;#39;, &amp;#39;events_url&amp;#39;: &amp;#39;https://api.github.com/events&amp;#39;, &amp;#39;feeds_url&amp;#39;: &amp;#39;https://api.github.com/feeds&amp;#39;, &amp;#39;followers_url&amp;#39;: &amp;#39;https://api.github.com/user/followers&amp;#39;, &amp;#39;following_url&amp;#39;: &amp;#39;https://api.github.com/user/following{/target}&amp;#39;, &amp;#39;gists_url&amp;#39;: &amp;#39;https://api.github.com/gists{/gist_id}&amp;#39;, &amp;#39;hub_url&amp;#39;: &amp;#39;https://api.github.com/hub&amp;#39;, &amp;#39;issue_search_url&amp;#39;: &amp;#39;https://api.github.com/search/issues?q={query}{&amp;amp;page,per_page,sort,order}&amp;#39;, &amp;#39;issues_url&amp;#39;: &amp;#39;https://api.github.com/issues&amp;#39;, &amp;#39;keys_url&amp;#39;: &amp;#39;https://api.github.com/user/keys&amp;#39;, &amp;#39;notifications_url&amp;#39;: &amp;#39;https://api.github.com/notifications&amp;#39;, &amp;#39;organization_repositories_url&amp;#39;: &amp;#39;https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}&amp;#39;, &amp;#39;organization_url&amp;#39;: &amp;#39;https://api.github.com/orgs/{org}&amp;#39;, &amp;#39;public_gists_url&amp;#39;: &amp;#39;https://api.github.com/gists/public&amp;#39;, &amp;#39;rate_limit_url&amp;#39;: &amp;#39;https://api.github.com/rate_limit&amp;#39;, &amp;#39;repository_url&amp;#39;: &amp;#39;https://api.github.com/repos/{owner}/{repo}&amp;#39;, &amp;#39;repository_search_url&amp;#39;: &amp;#39;https://api.github.com/search/repositories?q={query}{&amp;amp;page,per_page,sort,order}&amp;#39;, &amp;#39;current_user_repositories_url&amp;#39;: &amp;#39;https://api.github.com/user/repos{?type,page,per_page,sort}&amp;#39;, &amp;#39;starred_url&amp;#39;: &amp;#39;https://api.github.com/user/starred{/owner}{/repo}&amp;#39;, &amp;#39;starred_gists_url&amp;#39;: &amp;#39;https://api.github.com/gists/starred&amp;#39;, &amp;#39;team_url&amp;#39;: &amp;#39;https://api.github.com/teams&amp;#39;, &amp;#39;user_url&amp;#39;: &amp;#39;https://api.github.com/users/{user}&amp;#39;, &amp;#39;user_organizations_url&amp;#39;: &amp;#39;https://api.github.com/user/orgs&amp;#39;, &amp;#39;user_repositories_url&amp;#39;: &amp;#39;https://api.github.com/users/{user}/repos{?type,page,per_page,sort}&amp;#39;, &amp;#39;user_search_url&amp;#39;: &amp;#39;https://api.github.com/search/users?q={query}{&amp;amp;page,per_page,sort,order}&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;type&lt;/code&gt; of the return value of &lt;code&gt;.json()&lt;/code&gt; is a dictionary, so you can access values in the object by key.&lt;/p&gt;
&lt;p&gt;You can do a lot with status codes and message bodies. But, if you need more information, like metadata about the response itself, you&amp;rsquo;ll need to look at the response&amp;rsquo;s headers.&lt;/p&gt;
&lt;h3 id=&quot;headers&quot;&gt;Headers&lt;/h3&gt;
&lt;p&gt;The response headers can give you useful information, such as the content type of the response payload and a time limit on how long to cache the response. To view these headers, access &lt;code&gt;.headers&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;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;go&quot;&gt;{&amp;#39;Server&amp;#39;: &amp;#39;GitHub.com&amp;#39;, &amp;#39;Date&amp;#39;: &amp;#39;Mon, 10 Dec 2018 17:49:54 GMT&amp;#39;, &amp;#39;Content-Type&amp;#39;: &amp;#39;application/json; charset=utf-8&amp;#39;, &amp;#39;Transfer-Encoding&amp;#39;: &amp;#39;chunked&amp;#39;, &amp;#39;Status&amp;#39;: &amp;#39;200 OK&amp;#39;, &amp;#39;X-RateLimit-Limit&amp;#39;: &amp;#39;60&amp;#39;, &amp;#39;X-RateLimit-Remaining&amp;#39;: &amp;#39;59&amp;#39;, &amp;#39;X-RateLimit-Reset&amp;#39;: &amp;#39;1544467794&amp;#39;, &amp;#39;Cache-Control&amp;#39;: &amp;#39;public, max-age=60, s-maxage=60&amp;#39;, &amp;#39;Vary&amp;#39;: &amp;#39;Accept&amp;#39;, &amp;#39;ETag&amp;#39;: &amp;#39;W/&amp;quot;7dc470913f1fe9bb6c7355b50a0737bc&amp;quot;&amp;#39;, &amp;#39;X-GitHub-Media-Type&amp;#39;: &amp;#39;github.v3; format=json&amp;#39;, &amp;#39;Access-Control-Expose-Headers&amp;#39;: &amp;#39;ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type&amp;#39;, &amp;#39;Access-Control-Allow-Origin&amp;#39;: &amp;#39;*&amp;#39;, &amp;#39;Strict-Transport-Security&amp;#39;: &amp;#39;max-age=31536000; includeSubdomains; preload&amp;#39;, &amp;#39;X-Frame-Options&amp;#39;: &amp;#39;deny&amp;#39;, &amp;#39;X-Content-Type-Options&amp;#39;: &amp;#39;nosniff&amp;#39;, &amp;#39;X-XSS-Protection&amp;#39;: &amp;#39;1; mode=block&amp;#39;, &amp;#39;Referrer-Policy&amp;#39;: &amp;#39;origin-when-cross-origin, strict-origin-when-cross-origin&amp;#39;, &amp;#39;Content-Security-Policy&amp;#39;: &amp;quot;default-src &amp;#39;none&amp;#39;&amp;quot;, &amp;#39;Content-Encoding&amp;#39;: &amp;#39;gzip&amp;#39;, &amp;#39;X-GitHub-Request-Id&amp;#39;: &amp;#39;E439:4581:CF2351:1CA3E06:5C0EA741&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.headers&lt;/code&gt; returns a dictionary-like object, allowing you to access header values by key. For example, to see the content type of the response payload, you can access &lt;code&gt;Content-Type&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;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;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;go&quot;&gt;&amp;#39;application/json; charset=utf-8&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is something special about this dictionary-like headers object, though. The HTTP spec defines headers to be case-insensitive, which means we are able to access these headers without worrying about their capitalization:&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;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;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;go&quot;&gt;&amp;#39;application/json; charset=utf-8&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whether you use the key &lt;code&gt;&#39;content-type&#39;&lt;/code&gt; or &lt;code&gt;&#39;Content-Type&#39;&lt;/code&gt;, you&amp;rsquo;ll get the same value.&lt;/p&gt;
&lt;p&gt;Now, you&amp;rsquo;ve learned the basics about &lt;code&gt;Response&lt;/code&gt;. You&amp;rsquo;ve seen its most useful attributes and methods in action. Let&amp;rsquo;s take a step back and see how your responses change when you customize your &lt;code&gt;GET&lt;/code&gt; requests.&lt;/p&gt;
&lt;h2 id=&quot;query-string-parameters&quot;&gt;Query String Parameters&lt;/h2&gt;
&lt;p&gt;One common way to customize a &lt;code&gt;GET&lt;/code&gt; request is to pass values through &lt;a href=&quot;https://en.wikipedia.org/wiki/Query_string&quot;&gt;query string&lt;/a&gt; parameters in the URL. To do this using &lt;code&gt;get()&lt;/code&gt;, you pass data to &lt;code&gt;params&lt;/code&gt;. For example, you can use GitHub&amp;rsquo;s &lt;a href=&quot;https://developer.github.com/v3/search/&quot;&gt;Search&lt;/a&gt; API to look for the &lt;code&gt;requests&lt;/code&gt; library:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Search GitHub&amp;#39;s repositories for requests&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/search/repositories&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;params&lt;/span&gt;&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;q&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;requests+language:python&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;span class=&quot;c1&quot;&gt;# Inspect some attributes of the `requests` repository&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;json_response&lt;/span&gt; &lt;span class=&quot;o&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;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json_response&lt;/span&gt;&lt;span class=&quot;p&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;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;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Repository name: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{repository[&amp;quot;name&amp;quot;]}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Python 3.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;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Repository description: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{repository[&amp;quot;description&amp;quot;]}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Python 3.6+&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By passing the dictionary &lt;code&gt;{&#39;q&#39;: &#39;requests+language:python&#39;}&lt;/code&gt; to the &lt;code&gt;params&lt;/code&gt; parameter of &lt;code&gt;.get()&lt;/code&gt;, you are able to modify the results that come back from the Search API.&lt;/p&gt;
&lt;p&gt;You can pass &lt;code&gt;params&lt;/code&gt; to &lt;code&gt;get()&lt;/code&gt; in the form of a dictionary, as you have just done, or as a list of tuples:&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/search/repositories&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;params&lt;/span&gt;&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;q&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;requests+language:python&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;go&quot;&gt;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can even pass the values as &lt;code&gt;bytes&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/search/repositories&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;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;q=requests+language:python&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;go&quot;&gt;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Query strings are useful for parameterizing &lt;code&gt;GET&lt;/code&gt; requests. You can also customize your requests by adding or modifying the headers you send.&lt;/p&gt;
&lt;h2 id=&quot;request-headers&quot;&gt;Request Headers&lt;/h2&gt;
&lt;p&gt;To customize headers, you pass a dictionary of HTTP headers to &lt;code&gt;get()&lt;/code&gt; using the &lt;code&gt;headers&lt;/code&gt; parameter. For example, you can change your previous search request to highlight matching search terms in the results by specifying the &lt;code&gt;text-match&lt;/code&gt; media type in the &lt;code&gt;Accept&lt;/code&gt; header:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/search/repositories&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&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;q&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;requests+language:python&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;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;Accept&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/vnd.github.v3.text-match+json&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;span class=&quot;c1&quot;&gt;# View the new `text-matches` array which provides information&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# about your search term within the results&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;json_response&lt;/span&gt; &lt;span class=&quot;o&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;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json_response&lt;/span&gt;&lt;span class=&quot;p&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;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;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Text matches: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{repository[&amp;quot;text_matches&amp;quot;]}&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;The &lt;code&gt;Accept&lt;/code&gt; header tells the server what content types your application can handle. In this case, since you&amp;rsquo;re expecting the matching search terms to be highlighted, you&amp;rsquo;re using the header value &lt;code&gt;application/vnd.github.v3.text-match+json&lt;/code&gt;, which is a proprietary GitHub &lt;code&gt;Accept&lt;/code&gt; header where the content is a special JSON format.&lt;/p&gt;
&lt;p&gt;Before you learn more ways to customize requests, let&amp;rsquo;s broaden the horizon by exploring other HTTP methods.&lt;/p&gt;
&lt;h2 id=&quot;other-http-methods&quot;&gt;Other HTTP Methods&lt;/h2&gt;
&lt;p&gt;Aside from &lt;code&gt;GET&lt;/code&gt;, other popular HTTP methods include &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, and &lt;code&gt;OPTIONS&lt;/code&gt;. &lt;code&gt;requests&lt;/code&gt; provides a method, with a similar signature to &lt;code&gt;get()&lt;/code&gt;, for each of these HTTP methods:&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;requests&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;s1&quot;&gt;&amp;#39;https://httpbin.org/post&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;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;key&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;value&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://httpbin.org/put&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;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;key&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;value&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://httpbin.org/delete&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;requests&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;s1&quot;&gt;&amp;#39;https://httpbin.org/get&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://httpbin.org/patch&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;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;key&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;value&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://httpbin.org/get&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each function call makes a request to the &lt;code&gt;httpbin&lt;/code&gt; service using the corresponding HTTP method. For each method, you can inspect their responses in the same way you did 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;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://httpbin.org/get&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;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;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;go&quot;&gt;&amp;#39;application/json&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;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://httpbin.org/delete&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_response&lt;/span&gt; &lt;span class=&quot;o&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;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;args&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Headers, response bodies, status codes, and more are returned in the &lt;code&gt;Response&lt;/code&gt; for each method. Next you&amp;rsquo;ll take a closer look at the &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;PATCH&lt;/code&gt; methods and learn how they differ from the other request types.&lt;/p&gt;
&lt;h2 id=&quot;the-message-body&quot;&gt;The Message Body&lt;/h2&gt;
&lt;p&gt;According to the HTTP specification, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, and the less common &lt;code&gt;PATCH&lt;/code&gt; requests pass their data through the message body rather than through parameters in the query string. Using &lt;code&gt;requests&lt;/code&gt;, you&amp;rsquo;ll pass the payload to the corresponding function&amp;rsquo;s &lt;code&gt;data&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;data&lt;/code&gt; takes a dictionary, a list of tuples, bytes, or a file-like object. You&amp;rsquo;ll want to adapt the data you send in the body of your request to the specific needs of the service you&amp;rsquo;re interacting with.&lt;/p&gt;
&lt;p&gt;For example, if your request&amp;rsquo;s content type is &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;, you can send the form data as a 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;requests&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;s1&quot;&gt;&amp;#39;https://httpbin.org/post&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;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;key&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also send that same data as a list of tuples:&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;requests&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;s1&quot;&gt;&amp;#39;https://httpbin.org/post&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;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;key&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If, however, you need to send JSON data, you can use the &lt;code&gt;json&lt;/code&gt; parameter. When you pass JSON data via &lt;code&gt;json&lt;/code&gt;, &lt;code&gt;requests&lt;/code&gt; will serialize your data and add the correct &lt;code&gt;Content-Type&lt;/code&gt; header for you.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://httpbin.org/&quot;&gt;httpbin.org&lt;/a&gt; is a great resource created by the author of &lt;code&gt;requests&lt;/code&gt;, &lt;a href=&quot;https://realpython.com/interview-kenneth-reitz/&quot;&gt;Kenneth Reitz&lt;/a&gt;. It&amp;rsquo;s a service that accepts test requests and responds with data about the requests. For instance, you can use it to inspect a basic &lt;code&gt;POST&lt;/code&gt; request:&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;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://httpbin.org/post&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_response&lt;/span&gt; &lt;span class=&quot;o&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;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_response&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;go&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;headers&amp;#39;&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;go&quot;&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see from the response that the server received your request data and headers as you sent them. &lt;code&gt;requests&lt;/code&gt; also provides this information to you in the form of a &lt;code&gt;PreparedRequest&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;inspecting-your-request&quot;&gt;Inspecting Your Request&lt;/h2&gt;
&lt;p&gt;When you make a request, the &lt;code&gt;requests&lt;/code&gt; library prepares the request before actually sending it to the destination server. Request preparation includes things like validating headers and serializing JSON content.&lt;/p&gt;
&lt;p&gt;You can view the &lt;code&gt;PreparedRequest&lt;/code&gt; by accessing &lt;code&gt;.request&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;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://httpbin.org/post&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;value&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;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;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;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;go&quot;&gt;&amp;#39;application/json&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;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;https://httpbin.org/post&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;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;b&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Inspecting the &lt;code&gt;PreparedRequest&lt;/code&gt; gives you access to all kinds of information about the request being made such as payload, URL, headers, authentication, and more.&lt;/p&gt;
&lt;p&gt;So far, you&amp;rsquo;ve made a lot of different kinds of requests, but they&amp;rsquo;ve all had one thing in common: they&amp;rsquo;re unauthenticated requests to public APIs. Many services you may come across will want you to authenticate in some way.&lt;/p&gt;
&lt;h2 id=&quot;authentication&quot;&gt;Authentication&lt;/h2&gt;
&lt;p&gt;Authentication helps a service understand who you are. Typically, you provide your credentials to a server by passing data through the &lt;code&gt;Authorization&lt;/code&gt; header or a custom header defined by the service. All the request functions you&amp;rsquo;ve seen to this point provide a parameter called &lt;code&gt;auth&lt;/code&gt;, which allows you to pass your credentials.&lt;/p&gt;
&lt;p&gt;One example of an API that requires authentication is GitHub&amp;rsquo;s &lt;a href=&quot;https://developer.github.com/v3/users/#get-the-authenticated-user&quot;&gt;Authenticated User&lt;/a&gt; API. This endpoint provides information about the authenticated user&amp;rsquo;s profile. To make a request to the Authenticated User API, you can pass your GitHub username and password in a tuple to &lt;code&gt;get()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;getpass&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getpass&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/user&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auth&lt;/span&gt;&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;username&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getpass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The request succeeded if the credentials you passed in the tuple to &lt;code&gt;auth&lt;/code&gt; are valid. If you try to make this request with no credentials, you&amp;rsquo;ll see that the status code is &lt;code&gt;401 Unauthorized&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/user&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Response [401]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you pass your username and password in a tuple to the &lt;code&gt;auth&lt;/code&gt; parameter, &lt;code&gt;requests&lt;/code&gt; is applying the credentials using HTTP&amp;rsquo;s &lt;a href=&quot;https://en.wikipedia.org/wiki/Basic_access_authentication&quot;&gt;Basic access authentication scheme&lt;/a&gt; under the hood.&lt;/p&gt;
&lt;p&gt;Therefore, you could make the same request by passing explicit Basic authentication credentials using &lt;code&gt;HTTPBasicAuth&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTPBasicAuth&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;getpass&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getpass&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/user&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;auth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTPBasicAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getpass&lt;/span&gt;&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;go&quot;&gt;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Though you don&amp;rsquo;t need to be explicit for Basic authentication, you may want to authenticate using another method. &lt;code&gt;requests&lt;/code&gt; provides other methods of authentication out of the box such as &lt;code&gt;HTTPDigestAuth&lt;/code&gt; and &lt;code&gt;HTTPProxyAuth&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can even supply your own authentication mechanism. To do so, you must first create a subclass of &lt;code&gt;AuthBase&lt;/code&gt;. Then, you implement &lt;code&gt;__call__()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AuthBase&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TokenAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthBase&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;Implements a custom authentication scheme.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&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;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__call__&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;r&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;Attach an API token to a custom auth header.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;X-TokenAuth&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;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.token}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Python 3.6+&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://httpbin.org/get&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TokenAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;12345abcde-token&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, your custom &lt;code&gt;TokenAuth&lt;/code&gt; mechanism receives a token, then includes that token in the &lt;code&gt;X-TokenAuth&lt;/code&gt; header of your request.&lt;/p&gt;
&lt;p&gt;Bad authentication mechanisms can lead to security vulnerabilities, so unless a service requires a custom authentication mechanism for some reason, you&amp;rsquo;ll always want to use a tried-and-true auth scheme like Basic or OAuth.&lt;/p&gt;
&lt;p&gt;While you&amp;rsquo;re thinking about security, let&amp;rsquo;s consider dealing with SSL Certificates using &lt;code&gt;requests&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;ssl-certificate-verification&quot;&gt;SSL Certificate Verification&lt;/h2&gt;
&lt;p&gt;Any time the data you are trying to send or receive is sensitive, security is important. The way that you communicate with secure sites over HTTP is by establishing an encrypted connection using SSL, which means that verifying the target server&amp;rsquo;s SSL Certificate is critical.&lt;/p&gt;
&lt;p&gt;The good news is that &lt;code&gt;requests&lt;/code&gt; does this for you by default. However, there are some cases where you might want to change this behavior.&lt;/p&gt;
&lt;p&gt;If you want to disable SSL Certificate verification, you pass &lt;code&gt;False&lt;/code&gt; to the &lt;code&gt;verify&lt;/code&gt; parameter of the request function:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  InsecureRequestWarning)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;requests&lt;/code&gt; even warns you when you&amp;rsquo;re making an insecure request to help you keep your data safe!&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;a href=&quot;http://docs.python-requests.org/en/master/user/advanced/#ca-certificates&quot;&gt;&lt;code&gt;requests&lt;/code&gt; uses a package called &lt;code&gt;certifi&lt;/code&gt;&lt;/a&gt; to provide Certificate Authorities. This lets &lt;code&gt;requests&lt;/code&gt; know which authorities it can trust. Therefore, you should update &lt;code&gt;certifi&lt;/code&gt; frequently to keep your connections as secure as possible.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;performance&quot;&gt;Performance&lt;/h2&gt;
&lt;p&gt;When using &lt;code&gt;requests&lt;/code&gt;, especially in a production application environment, it&amp;rsquo;s important to consider performance implications. Features like timeout control, sessions, and retry limits can help you keep your application running smoothly.&lt;/p&gt;
&lt;h3 id=&quot;timeouts&quot;&gt;Timeouts&lt;/h3&gt;
&lt;p&gt;When you make an inline request to an external service, your system will need to wait upon the response before moving on. If your application waits too long for that response, requests to your service could back up, your user experience could suffer, or your background jobs could hang.&lt;/p&gt;
&lt;p&gt;By default, &lt;code&gt;requests&lt;/code&gt; will wait indefinitely on the response, so you should almost always specify a timeout duration to prevent these things from happening. To set the request&amp;rsquo;s timeout, use the &lt;code&gt;timeout&lt;/code&gt; parameter. &lt;code&gt;timeout&lt;/code&gt; can be an integer or float representing the number of seconds to wait on a response before timing out:&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;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;go&quot;&gt;&amp;lt;Response [200]&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.05&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the first request, the request will timeout after 1 second. In the second request, the request will timeout after 3.05 seconds.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://docs.python-requests.org/en/master/user/advanced/#timeouts&quot;&gt;You can also pass a tuple&lt;/a&gt; to &lt;code&gt;timeout&lt;/code&gt; with the first element being a connect timeout (the time it allows for the client to establish a connection to the server), and the second being a read timeout (the time it will wait on a response once your client has established a connection):&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;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;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;&amp;lt;Response [200]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the request establishes a connection within 2 seconds and receives data within 5 seconds of the connection being established, then the response will be returned as it was before. If the request times out, then the function will raise a &lt;code&gt;Timeout&lt;/code&gt; exception:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;try&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;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;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;except&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;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 request timed out&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;The request did not time out&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your program can catch the &lt;code&gt;Timeout&lt;/code&gt; exception and respond accordingly.&lt;/p&gt;
&lt;h3 id=&quot;the-session-object&quot;&gt;The Session Object&lt;/h3&gt;
&lt;p&gt;Until now, you&amp;rsquo;ve been dealing with high level &lt;code&gt;requests&lt;/code&gt; APIs such as &lt;code&gt;get()&lt;/code&gt; and &lt;code&gt;post()&lt;/code&gt;. These functions are abstractions of what&amp;rsquo;s going on when you make your requests. They hide implementation details such as how connections are managed so that you don&amp;rsquo;t have to worry about them.&lt;/p&gt;
&lt;p&gt;Underneath those abstractions is a class called &lt;code&gt;Session&lt;/code&gt;. If you need to fine-tune your control over how requests are being made or improve the performance of your requests, you may need to use a &lt;code&gt;Session&lt;/code&gt; instance directly.&lt;/p&gt;
&lt;p&gt;Sessions are used to persist parameters across requests. For example, if you want to use the same authentication across multiple requests, you could use a session:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;getpass&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getpass&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# By using a context manager, you can ensure the resources used by&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# the session will be released after use&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth&lt;/span&gt; &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;username&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getpass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Instead of requests.get(), you&amp;#39;ll use session.get()&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;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/user&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# You can inspect the response just like you did before&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;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;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each time you make a request with &lt;code&gt;session&lt;/code&gt;, once it has been initialized with authentication credentials, the credentials will be persisted.&lt;/p&gt;
&lt;p&gt;The primary performance optimization of sessions comes in the form of persistent connections. When your app makes a connection to a server using a &lt;code&gt;Session&lt;/code&gt;, it keeps that connection around in a connection pool. When your app wants to connect to the same server again, it will reuse a connection from the pool rather than establishing a new one.&lt;/p&gt;
&lt;h3 id=&quot;max-retries&quot;&gt;Max Retries&lt;/h3&gt;
&lt;p&gt;When a request fails, you may want your application to retry the same request. However, &lt;code&gt;requests&lt;/code&gt; will not do this for you by default. To apply this functionality, you need to implement a custom &lt;a href=&quot;http://docs.python-requests.org/en/master/user/advanced/#transport-adapters&quot;&gt;Transport Adapter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Transport Adapters let you define a set of configurations per service you&amp;rsquo;re interacting with. For example, let&amp;rsquo;s say you want all requests to &lt;code&gt;https://api.github.com&lt;/code&gt; to retry three times before finally raising a &lt;code&gt;ConnectionError&lt;/code&gt;. You would build a Transport Adapter, set its &lt;code&gt;max_retries&lt;/code&gt; parameter, and mount it to an existing &lt;code&gt;Session&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.adapters&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTPAdapter&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ConnectionError&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;github_adapter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTPAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_retries&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Use `github_adapter` for all requests to endpoints that start with this URL&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;github_adapter&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;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com&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;ConnectionError&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ce&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;ce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you mount the &lt;code&gt;HTTPAdapter&lt;/code&gt;, &lt;code&gt;github_adapter&lt;/code&gt;, to &lt;code&gt;session&lt;/code&gt;, &lt;code&gt;session&lt;/code&gt; will adhere to its configuration for each request to https://api.github.com.&lt;/p&gt;
&lt;p&gt;Timeouts, Transport Adapters, and sessions are for keeping your code efficient and your application resilient.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve come a long way in learning about Python&amp;rsquo;s powerful &lt;code&gt;requests&lt;/code&gt; library.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;re now able to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make requests using a variety of different HTTP methods such as &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, and &lt;code&gt;PUT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Customize your requests by modifying headers, authentication, query strings, and message bodies&lt;/li&gt;
&lt;li&gt;Inspect the data you send to the server and the data the server sends back to you&lt;/li&gt;
&lt;li&gt;Work with SSL Certificate verification&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;requests&lt;/code&gt; effectively using &lt;code&gt;max_retries&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, Sessions, and Transport Adapters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because you learned how to use &lt;code&gt;requests&lt;/code&gt;, you&amp;rsquo;re equipped to explore the wide world of web services and build awesome applications using the fascinating data they provide.&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>Working With Files in Python</title>
      <id>https://realpython.com/working-with-files-in-python/</id>
      <link href="https://realpython.com/working-with-files-in-python/"/>
      <updated>2019-01-21T06:00:00+00:00</updated>
      <summary>In this tutorial, you&#39;ll learn how you can work with files in Python by using built-in modules to perform practical tasks that involve groups of files, like renaming them, moving them around, archiving them, and getting their metadata.</summary>
      <content type="html">
        &lt;p&gt;Python has several built-in modules and functions for handling files. These functions are spread out over several modules such as &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;os.path&lt;/code&gt;, &lt;code&gt;shutil&lt;/code&gt;, and &lt;code&gt;pathlib&lt;/code&gt;, to name a few. This article gathers in one place many of the functions you need to know in order to perform the most common operations on files in 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;Retrieve file properties&lt;/li&gt;
&lt;li&gt;Create directories&lt;/li&gt;
&lt;li&gt;Match patterns in filenames&lt;/li&gt;
&lt;li&gt;Traverse directory trees&lt;/li&gt;
&lt;li&gt;Make temporary files and directories&lt;/li&gt;
&lt;li&gt;Delete files and directories&lt;/li&gt;
&lt;li&gt;Copy, move, or rename files and directories&lt;/li&gt;
&lt;li&gt;Create and extract ZIP and TAR archives&lt;/li&gt;
&lt;li&gt;Open multiple files using the &lt;code&gt;fileinput&lt;/code&gt; module&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;reading-and-writing-data-to-files-in-python&quot;&gt;Reading and Writing Data to Files in Python&lt;/h2&gt;
&lt;p&gt;Reading and writing data to files using Python is pretty straightforward. To do this, you must first open files in the appropriate mode. Here&amp;rsquo;s an example of how to open a text file and read its contents:&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;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;s1&quot;&gt;&amp;#39;data.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;r&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;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;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;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;open()&lt;/code&gt; takes a filename and a mode as its arguments. &lt;code&gt;r&lt;/code&gt; opens the file in read only mode. To write data to a file, pass in &lt;code&gt;w&lt;/code&gt; as an argument 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;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;s1&quot;&gt;&amp;#39;data.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;w&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;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;some data to be written to the file&amp;#39;&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;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the examples above, &lt;code&gt;open()&lt;/code&gt; opens files for reading or writing and returns a file handle (&lt;code&gt;f&lt;/code&gt; in this case) that provides methods that can be used to read or write data to the file. Read &lt;a href=&quot;https://dbader.org/blog/python-file-io&quot;&gt;Working With File I/O in Python&lt;/a&gt; for more information on how to read and write to files.&lt;/p&gt;
&lt;h2 id=&quot;getting-a-directory-listing&quot;&gt;Getting a Directory Listing&lt;/h2&gt;
&lt;p&gt;Suppose your current working directory has a subdirectory called &lt;code&gt;my_directory&lt;/code&gt; that has the following contents:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;my_directory/
|
├── sub_dir/
|   ├── bar.py
|   └── foo.py
|
├── sub_dir_b/
|   └── file4.txt
|
├── sub_dir_c/
|   ├── config.py
|   └── file5.txt
|
├── file1.py
├── file2.csv
└── file3.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The built-in &lt;code&gt;os&lt;/code&gt; module has a number of useful functions that can be used to list directory contents and filter the results. To get a list of all the files and folders in a particular directory in the filesystem, use &lt;code&gt;os.listdir()&lt;/code&gt; in legacy versions of Python or &lt;code&gt;os.scandir()&lt;/code&gt; in Python 3.x. &lt;code&gt;os.scandir()&lt;/code&gt; is the preferred method to use if you also want to get file and directory properties such as file size and modification date.&lt;/p&gt;
&lt;h3 id=&quot;directory-listing-in-legacy-python-versions&quot;&gt;Directory Listing in Legacy Python Versions&lt;/h3&gt;
&lt;p&gt;In versions of Python prior to Python 3, &lt;code&gt;os.listdir()&lt;/code&gt; is the method to use to get a directory listing:&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;os&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entries&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;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;os.listdir()&lt;/code&gt; returns a Python list containing the names of the files and subdirectories in the directory given by the path argument:&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;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;sub_dir_c&amp;#39;, &amp;#39;file1.py&amp;#39;, &amp;#39;sub_dir_b&amp;#39;, &amp;#39;file3.txt&amp;#39;, &amp;#39;file2.csv&amp;#39;, &amp;#39;sub_dir&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A directory listing like that isn&amp;rsquo;t easy to read. Printing out the output of a call to &lt;code&gt;os.listdir()&lt;/code&gt; using a loop helps clean things up:&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;entries&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;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&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;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir_c&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file1.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir_b&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file3.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file2.csv&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;directory-listing-in-modern-python-versions&quot;&gt;Directory Listing in Modern Python Versions&lt;/h3&gt;
&lt;p&gt;In modern versions of Python, an alternative to &lt;code&gt;os.listdir()&lt;/code&gt; is to use &lt;code&gt;os.scandir()&lt;/code&gt; and &lt;code&gt;pathlib.Path()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;os.scandir()&lt;/code&gt; was introduced in Python 3.5 and is documented in &lt;a href=&quot;https://www.python.org/dev/peps/pep-0471/&quot;&gt;PEP 471&lt;/a&gt;. &lt;code&gt;os.scandir()&lt;/code&gt; returns an iterator as opposed to a list when called:&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;os&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entries&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;scandir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&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;entries&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;posix.ScandirIterator object at 0x7f5b047f3690&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;ScandirIterator&lt;/code&gt; points to all the entries in the current directory. You can loop over the contents of the iterator and print out the filenames:&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;k&quot;&gt;with&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;scandir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&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;entries&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;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&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;entry&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;os.scandir()&lt;/code&gt; is used in conjunction with the &lt;code&gt;with&lt;/code&gt; statement because it supports the context manager protocol. Using a context manager closes the iterator and frees up acquired resources automatically after the iterator has been exhausted. The result is a print out of the filenames in &lt;code&gt;my_directory/&lt;/code&gt; just like you saw in the &lt;code&gt;os.listdir()&lt;/code&gt; example:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;sub_dir_c&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file1.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir_b&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file3.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file2.csv&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another way to get a directory listing is to use the &lt;code&gt;pathlib&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;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;span class=&quot;n&quot;&gt;entries&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;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&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;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterdir&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;entry&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;The objects returned by &lt;code&gt;Path&lt;/code&gt; are either &lt;code&gt;PosixPath&lt;/code&gt; or &lt;code&gt;WindowsPath&lt;/code&gt; objects depending on the OS. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;pathlib.Path()&lt;/code&gt; objects have an &lt;code&gt;.iterdir()&lt;/code&gt; method for creating an iterator of all files and folders in a directory. Each entry yielded by &lt;code&gt;.iterdir()&lt;/code&gt; contains information about the file or directory such as its name and file attributes. &lt;code&gt;pathlib&lt;/code&gt; was first introduced in Python 3.4 and is a great addition to Python that provides an object oriented interface to the filesystem.&lt;/p&gt;
&lt;p&gt;In the example above, you call &lt;code&gt;pathlib.Path()&lt;/code&gt; and pass a path argument to it. Next is the call to &lt;code&gt;.iterdir()&lt;/code&gt; to get a list of all files and directories in &lt;code&gt;my_directory&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pathlib&lt;/code&gt; offers a set of classes featuring most of the common operations on paths in an easy, object-oriented way. Using &lt;code&gt;pathlib&lt;/code&gt; is more if not equally efficient as using the functions in &lt;code&gt;os&lt;/code&gt;. Another benefit of using &lt;code&gt;pathlib&lt;/code&gt; over &lt;code&gt;os&lt;/code&gt; is that it reduces the number of imports you need to make to manipulate filesystem paths. For more information, read &lt;a href=&quot;https://realpython.com/python-pathlib/&quot;&gt;Python 3&amp;rsquo;s pathlib Module: Taming the File System&lt;/a&gt;.  &lt;/p&gt;
&lt;p&gt;Running the code above produces 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;go&quot;&gt;sub_dir_c&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file1.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir_b&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file3.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file2.csv&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;pathlib.Path()&lt;/code&gt; or &lt;code&gt;os.scandir()&lt;/code&gt; instead of &lt;code&gt;os.listdir()&lt;/code&gt; is the preferred way of getting a directory listing, especially when you&amp;rsquo;re working with code that needs the file type and file attribute information. &lt;code&gt;pathlib.Path()&lt;/code&gt; offers much of the file and path handling functionality found in &lt;code&gt;os&lt;/code&gt; and &lt;code&gt;shutil&lt;/code&gt;, and it&amp;rsquo;s methods are more efficient than some found in these modules. We will discuss how to get file properties shortly.&lt;/p&gt;
&lt;p&gt;Here are the directory-listing functions again:&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;Function&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;os.listdir()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns a list of all files and folders in a directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;os.scandir()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns an iterator of all the objects in a directory including file attribute information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pathlib.Path.iterdir()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns an iterator of all the objects in a directory including file attribute information&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;These functions return a list of &lt;em&gt;everything&lt;/em&gt; in the directory, including subdirectories. This might not always be the behavior you want. The next section will show you how to filter the results from a directory listing.&lt;/p&gt;
&lt;h3 id=&quot;listing-all-files-in-a-directory&quot;&gt;Listing All Files in a Directory&lt;/h3&gt;
&lt;p&gt;This section will show you how to print out the names of files in a directory using &lt;code&gt;os.listdir()&lt;/code&gt;, &lt;code&gt;os.scandir()&lt;/code&gt;, and &lt;code&gt;pathlib.Path()&lt;/code&gt;. To filter out directories and only list files from a directory listing produced by &lt;code&gt;os.listdir()&lt;/code&gt;, use &lt;code&gt;os.path&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;os&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# List all files in a directory using os.listdir&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;basepath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basepath&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;os&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isfile&lt;/span&gt;&lt;span class=&quot;p&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;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&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;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, the call to &lt;code&gt;os.listdir()&lt;/code&gt; returns a list of everything in the specified path, and then that list is filtered by &lt;code&gt;os.path.isfile()&lt;/code&gt; to only print out files and not directories. This produces the following output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;file1.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file3.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file2.csv&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;An easier way to list files in a directory is to use &lt;code&gt;os.scandir()&lt;/code&gt; or &lt;code&gt;pathlib.Path()&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;os&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# List all files in a directory using scandir()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;basepath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;with&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;scandir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basepath&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;entries&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;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&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;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_file&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;entry&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;os.scandir()&lt;/code&gt; has the advantage of looking cleaner and being easier to understand than using &lt;code&gt;os.listdir()&lt;/code&gt;, even though it is one line of code longer. Calling &lt;code&gt;entry.is_file()&lt;/code&gt; on each item in the &lt;code&gt;ScandirIterator&lt;/code&gt;  returns &lt;code&gt;True&lt;/code&gt; if the object is a file. Printing out the names of all files in the directory gives you the following output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;file1.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file3.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file2.csv&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s how to list files in a directory using &lt;code&gt;pathlib.Path()&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;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;span class=&quot;n&quot;&gt;basepath&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;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;files_in_basepath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;basepath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files_in_basepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_file&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;item&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, you call &lt;code&gt;.is_file()&lt;/code&gt; on each entry yielded by &lt;code&gt;.iterdir()&lt;/code&gt;. The output produced is the same:&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;file1.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file3.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file2.csv&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code above can be made more concise if you combine the &lt;code&gt;for&lt;/code&gt; loop and the &lt;code&gt;if&lt;/code&gt; statement into a single generator expression. Dan Bader has an &lt;a href=&quot;https://dbader.org/blog/python-generator-expressions&quot;&gt;excellent article&lt;/a&gt; on &lt;a href=&quot;https://realpython.com/introduction-to-python-generators/&quot;&gt;generator expressions&lt;/a&gt; and list comprehensions.&lt;/p&gt;
&lt;p&gt;The modified version 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;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;span class=&quot;c1&quot;&gt;# List all files in directory using pathlib&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;basepath&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;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;files_in_basepath&lt;/span&gt; &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;entry&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;basepath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterdir&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;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files_in_basepath&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;item&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;This produces exactly the same output as the example before it. This section showed that filtering files or directories using &lt;code&gt;os.scandir()&lt;/code&gt; and &lt;code&gt;pathlib.Path()&lt;/code&gt; feels more intuitive and looks cleaner than using &lt;code&gt;os.listdir()&lt;/code&gt; in conjunction with &lt;code&gt;os.path&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;listing-subdirectories&quot;&gt;Listing Subdirectories&lt;/h3&gt;
&lt;p&gt;To list subdirectories instead of files, use one of the methods below. Here&amp;rsquo;s how to use &lt;code&gt;os.listdir()&lt;/code&gt; and &lt;code&gt;os.path()&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;os&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# List all subdirectories using os.listdir&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;basepath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basepath&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;os&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isdir&lt;/span&gt;&lt;span class=&quot;p&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;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&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;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Manipulating filesystem paths this way can quickly become cumbersome when you have multiple calls to &lt;code&gt;os.path.join()&lt;/code&gt;. Running this on my computer produces the following output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;sub_dir_c&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir_b&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s how to use &lt;code&gt;os.scandir()&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;os&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# List all subdirectories using scandir()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;basepath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;with&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;scandir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basepath&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;entries&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;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&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;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_dir&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;entry&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;As in the file listing example, here you call &lt;code&gt;.is_dir()&lt;/code&gt; on each entry returned by &lt;code&gt;os.scandir()&lt;/code&gt;. If the entry is a directory, &lt;code&gt;.is_dir()&lt;/code&gt; returns &lt;code&gt;True&lt;/code&gt;, and the directory&amp;rsquo;s name is printed out. The output is the same as above:&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;sub_dir_c&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir_b&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s how to use &lt;code&gt;pathlib.Path()&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;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;span class=&quot;c1&quot;&gt;# List all subdirectory using pathlib&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;basepath&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;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&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;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;basepath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterdir&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;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_dir&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;entry&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code&gt;.is_dir()&lt;/code&gt; on each entry of the &lt;code&gt;basepath&lt;/code&gt; iterator checks if an entry is a file or a directory. If the entry is a directory, its name is printed out to the screen, and the output produced is the same as the one from the previous example:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;sub_dir_c&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir_b&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;getting-file-attributes&quot;&gt;Getting File Attributes&lt;/h2&gt;
&lt;p&gt;Python makes retrieving file attributes such as file size and modified times easy. This is done through &lt;code&gt;os.stat()&lt;/code&gt;, &lt;code&gt;os.scandir()&lt;/code&gt;, or &lt;code&gt;pathlib.Path()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;os.scandir()&lt;/code&gt; and &lt;code&gt;pathlib.Path()&lt;/code&gt; retrieve a directory listing with file attributes combined. This can be potentially more efficient than using &lt;code&gt;os.listdir()&lt;/code&gt; to list files and then getting file attribute information for each file.&lt;/p&gt;
&lt;p&gt;The examples below show how to get the time the files in &lt;code&gt;my_directory/&lt;/code&gt; were last modified. The output is in seconds:&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;os&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scandir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&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;dir_contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dir_contents&lt;/span&gt;&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;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;&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;info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_mtime&lt;/span&gt;&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;1539032199.0052035&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1539032469.6324475&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1538998552.2402923&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1540233322.4009316&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1537192240.0497339&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1540266380.3434134&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;os.scandir()&lt;/code&gt; returns a &lt;code&gt;ScandirIterator&lt;/code&gt; object. Each entry in a &lt;code&gt;ScandirIterator&lt;/code&gt; object has a &lt;code&gt;.stat()&lt;/code&gt; method that retrieves information about the file or directory it points to. &lt;code&gt;.stat()&lt;/code&gt; provides information such as file size and the time of last modification. In the example above, the code prints out the &lt;code&gt;st_mtime&lt;/code&gt; attribute, which is the time the content of the file was last modified.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;pathlib&lt;/code&gt; module has corresponding methods for retrieving file information that give the same results:&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;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;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_dir&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;s1&quot;&gt;&amp;#39;my_directory&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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterdir&lt;/span&gt;&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;info&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;&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;info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_mtime&lt;/span&gt;&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;1539032199.0052035&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1539032469.6324475&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1538998552.2402923&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1540233322.4009316&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1537192240.0497339&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1540266380.3434134&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the example above, the code loops through the object returned by &lt;code&gt;.iterdir()&lt;/code&gt; and retrieves file attributes through a &lt;code&gt;.stat()&lt;/code&gt; call for each file in the directory list. The &lt;code&gt;st_mtime&lt;/code&gt; attribute returns a float value that represents &lt;a href=&quot;https://en.wikipedia.org/wiki/Epoch_(reference_date)#Computing&quot;&gt;seconds since the epoch&lt;/a&gt;. To convert the values returned by &lt;code&gt;st_mtime&lt;/code&gt; for display purposes, you could write a helper function to convert the seconds into a &lt;code&gt;datetime&lt;/code&gt; object:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;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;scandir&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;convert_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utcfromtimestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;formated_date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; %b %Y&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;formated_date&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dir_entries&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scandir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_directory/&amp;#39;&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;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dir_entries&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;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&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;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{entry.name}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; Last Modified: {convert_date(info.st_mtime)}&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 will first get a list of files in &lt;code&gt;my_directory&lt;/code&gt; and their attributes and then call &lt;code&gt;convert_date()&lt;/code&gt; to convert each file&amp;rsquo;s last modified time into a human readable form. &lt;code&gt;convert_date()&lt;/code&gt; makes use of &lt;code&gt;.strftime()&lt;/code&gt; to convert the time in seconds into a string.&lt;/p&gt;
&lt;p&gt;The arguments passed to &lt;code&gt;.strftime()&lt;/code&gt; are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;%d&lt;/code&gt;:&lt;/strong&gt; the day of the month&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;%b&lt;/code&gt;:&lt;/strong&gt; the month, in abbreviated form&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;%Y&lt;/code&gt;:&lt;/strong&gt; the year&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together, these directives produce output that looks 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;n&quot;&gt;get_files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file1.py        Last modified:  04 Oct 2018&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file3.txt       Last modified:  17 Sep 2018&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file2.txt       Last modified:  17 Sep 2018&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The syntax for converting dates and times into strings can be quite confusing. To read more about it, check out the &lt;a href=&quot;https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior&quot;&gt;official documentation&lt;/a&gt; on it. Another handy reference that is easy to remember is &lt;a href=&quot;http://strftime.org/&quot;&gt;http://strftime.org/&lt;/a&gt; .&lt;/p&gt;
&lt;h2 id=&quot;making-directories&quot;&gt;Making Directories&lt;/h2&gt;
&lt;p&gt;Sooner or later, the programs you write will have to create directories in order to store data in them. &lt;code&gt;os&lt;/code&gt;  and &lt;code&gt;pathlib&lt;/code&gt; include functions for creating directories. We&amp;rsquo;ll consider these:&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;Function&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;os.mkdir()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a single subdirectory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pathlib.Path.mkdir()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates single or multiple directories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;os.makedirs()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates multiple directories, including intermediate directories&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;creating-a-single-directory&quot;&gt;Creating a Single Directory&lt;/h3&gt;
&lt;p&gt;To create a single directory, pass a path to the directory as a parameter to &lt;code&gt;os.mkdir()&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;os&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;mkdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;example_directory/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If a directory already exists, &lt;code&gt;os.mkdir()&lt;/code&gt; raises &lt;code&gt;FileExistsError&lt;/code&gt;. Alternatively, you can create a directory using &lt;code&gt;pathlib&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;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;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;example_directory/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mkdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the path already exists, &lt;code&gt;mkdir()&lt;/code&gt; raises a &lt;code&gt;FileExistsError&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;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mkdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;  File &amp;#39;&amp;lt;stdin&amp;gt;&amp;#39;, line 1, in &amp;lt;module&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;  File &amp;#39;/usr/lib/python3.5/pathlib.py&amp;#39;, line 1214, in mkdir&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;    self._accessor.mkdir(self, mode)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;  File &amp;#39;/usr/lib/python3.5/pathlib.py&amp;#39;, line 371, in wrapped&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;    return strfunc(str(pathobj), *args)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;FileExistsError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;[Errno 17] File exists: &amp;#39;.&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[Errno 17] File exists: &amp;#39;.&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To avoid errors like this, &lt;a href=&quot;https://realpython.com/python-exceptions/&quot;&gt;catch the error&lt;/a&gt; when it happens and let your user know:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pathlin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;example_directory&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;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mkdir&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;FileExistsError&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;nb&quot;&gt;print&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;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, you can ignore the &lt;code&gt;FileExistsError&lt;/code&gt; by passing the &lt;code&gt;exist_ok=True&lt;/code&gt; argument to &lt;code&gt;.mkdir()&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;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;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;example_directory&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mkdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exist_ok&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will not raise an error if the directory already exists.&lt;/p&gt;
&lt;h3 id=&quot;creating-multiple-directories&quot;&gt;Creating Multiple Directories&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;os.makedirs()&lt;/code&gt; is similar to &lt;code&gt;os.mkdir()&lt;/code&gt;. The difference between the two is that not only can &lt;code&gt;os.makedirs()&lt;/code&gt; create individual directories, it can also be used to create directory trees. In other words, it can create any necessary intermediate folders in order to ensure a full path exists.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;os.makedirs()&lt;/code&gt; is similar to running &lt;code&gt;mkdir -p&lt;/code&gt; in Bash. For example, to create a group of directories like &lt;code&gt;2018/10/05&lt;/code&gt;, all you have to do is the following:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&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;makedirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;2018/10/05&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 will create a nested directory structure that contains the folders 2018, 10, and 05:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.
|
└── 2018/
    └── 10/
        └── 05/
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.makedirs()&lt;/code&gt; creates directories with default permissions. If you need to create directories with different permissions call &lt;code&gt;.makedirs()&lt;/code&gt; and pass in the mode you would like the directories to be created in:&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;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makedirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;2018/10/05&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;0o770&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This creates the &lt;code&gt;2018/10/05&lt;/code&gt; directory structure and gives the owner and group users read, write, and execute permissions. The default mode is &lt;code&gt;0o777&lt;/code&gt;, and the file permission bits of existing parent directories are not changed. For more details on file permissions, and how the mode is applied, &lt;a href=&quot;https://docs.python.org/3/library/os.html#os.makedirs&quot;&gt;see the docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;tree&lt;/code&gt; to confirm that the right permissions were applied:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; tree -p -i .
&lt;span class=&quot;go&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[drwxrwx---]  2018&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[drwxrwx---]  10&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[drwxrwx---]  05&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This prints out a directory tree of the current directory. &lt;code&gt;tree&lt;/code&gt; is normally used to list contents of directories in a tree-like format. Passing the &lt;code&gt;-p&lt;/code&gt; and &lt;code&gt;-i&lt;/code&gt; arguments to it prints out the directory names and their file permission information in a vertical list. &lt;code&gt;-p&lt;/code&gt; prints out the file permissions, and &lt;code&gt;-i&lt;/code&gt; makes &lt;code&gt;tree&lt;/code&gt; produce a vertical list without indentation lines.&lt;/p&gt;
&lt;p&gt;As you can see, all of the directories have &lt;code&gt;770&lt;/code&gt; permissions. An alternative way to create directories is to use &lt;code&gt;.mkdir()&lt;/code&gt; from &lt;code&gt;pathlib.Path&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;pathlib&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;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;s1&quot;&gt;&amp;#39;2018/10/05&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mkdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Passing &lt;code&gt;parents=True&lt;/code&gt; to &lt;code&gt;Path.mkdir()&lt;/code&gt; makes it create the directory &lt;code&gt;05&lt;/code&gt; and any parent directories necessary to make the path valid.&lt;/p&gt;
&lt;p&gt;By default, &lt;code&gt;os.makedirs()&lt;/code&gt; and &lt;code&gt;Path.mkdir()&lt;/code&gt; raise an &lt;code&gt;OSError&lt;/code&gt; if the target directory already exists. This behavior can be overridden (as of Python 3.2) by passing &lt;code&gt;exist_ok=True&lt;/code&gt; as a keyword argument when calling each function. &lt;/p&gt;
&lt;p&gt;Running the code above produces a directory structure like the one below in one go:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.
|
└── 2018/
    └── 10/
        └── 05/
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I prefer using &lt;code&gt;pathlib&lt;/code&gt; when creating directories because I can use the same function to create single or nested directories.&lt;/p&gt;
&lt;h2 id=&quot;filename-pattern-matching&quot;&gt;Filename Pattern Matching&lt;/h2&gt;
&lt;p&gt;After getting a list of files in a directory using one of the methods above, you will most probably want to search for files that match a particular pattern. &lt;/p&gt;
&lt;p&gt;These are the methods and functions available to you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;endswith()&lt;/code&gt; and &lt;code&gt;startswith()&lt;/code&gt; string methods&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fnmatch.fnmatch()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;glob.glob()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pathlib.Path.glob()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these is discussed below. The examples in this section will be performed on a directory called &lt;code&gt;some_directory&lt;/code&gt; that has the following structure:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.
|
├── sub_dir/
|   ├── file1.py
|   └── file2.py
|
├── admin.py
├── data_01_backup.txt
├── data_01.txt
├── data_02_backup.txt
├── data_02.txt
├── data_03_backup.txt
├── data_03.txt
└── tests.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you&amp;rsquo;re following along using a Bash shell, you can create the above directory structure using the following commands:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mkdir some_directory
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; some_directory/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mkdir sub_dir
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; touch sub_dir/file1.py sub_dir/file2.py
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; touch data_&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;..03&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;.txt data_&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;..03&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;_backup.txt admin.py tests.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create the &lt;code&gt;some_directory/&lt;/code&gt; directory, change into it, and then create &lt;code&gt;sub_dir&lt;/code&gt;. The next line creates &lt;code&gt;file1.py&lt;/code&gt; and &lt;code&gt;file2.py&lt;/code&gt; in &lt;code&gt;sub_dir&lt;/code&gt;, and the last line creates all the other files using expansion. To learn more about shell expansion, visit &lt;a href=&quot;http://linuxcommand.org/lc3_lts0080.php&quot;&gt;this site&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;using-string-methods&quot;&gt;Using String Methods&lt;/h3&gt;
&lt;p&gt;Python has several built-in methods for &lt;a href=&quot;https://realpython.com/python-strings/&quot;&gt;modifying and manipulating strings&lt;/a&gt;. Two of these methods, &lt;code&gt;.startswith()&lt;/code&gt; and &lt;code&gt;.endswith()&lt;/code&gt;, are useful when you&amp;rsquo;re searching for patterns in filenames. To do this, first get a directory listing and then iterate over 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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&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;# Get .txt files&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;f_name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;some_directory&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;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f_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;.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;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_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code above finds all the files in &lt;code&gt;some_directory/&lt;/code&gt;, iterates over them and uses &lt;code&gt;.endswith()&lt;/code&gt; to print out the filenames that have the &lt;code&gt;.txt&lt;/code&gt; file extension. Running this on my computer produces the following output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;data_01.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_03.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_03_backup.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_02_backup.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_02.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_01_backup.txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;simple-filename-pattern-matching-using-fnmatch&quot;&gt;Simple Filename Pattern Matching Using &lt;code&gt;fnmatch&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;String methods are limited in their matching abilities. &lt;code&gt;fnmatch&lt;/code&gt; has more advanced functions and methods for pattern matching. We will consider &lt;code&gt;fnmatch.fnmatch()&lt;/code&gt;, a function that supports the use of wildcards such as &lt;code&gt;*&lt;/code&gt; and &lt;code&gt;?&lt;/code&gt; to match filenames. For example, in order to find all &lt;code&gt;.txt&lt;/code&gt; files in a directory using &lt;code&gt;fnmatch&lt;/code&gt;, you would 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;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&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;fnmatch&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;file_name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;some_directory/&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;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fnmatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fnmatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;s1&quot;&gt;&amp;#39;*.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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;This iterates over the list of files in &lt;code&gt;some_directory&lt;/code&gt; and uses &lt;code&gt;.fnmatch()&lt;/code&gt; to perform a wildcard search for files that have the &lt;code&gt;.txt&lt;/code&gt; extension.&lt;/p&gt;
&lt;h3 id=&quot;more-advanced-pattern-matching&quot;&gt;More Advanced Pattern Matching&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s suppose you want to find &lt;code&gt;.txt&lt;/code&gt; files that meet certain criteria. For example, you could be only interested in finding &lt;code&gt;.txt&lt;/code&gt; files that contain the word &lt;code&gt;data&lt;/code&gt;, a number between a set of underscores, and the word &lt;code&gt;backup&lt;/code&gt; in their filename. Something similar to &lt;code&gt;data_01_backup&lt;/code&gt;, &lt;code&gt;data_02_backup&lt;/code&gt;, or &lt;code&gt;data_03_backup&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;fnmatch.fnmatch()&lt;/code&gt;, you could do it this way:&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;filename&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;listdir&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;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fnmatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fnmatch&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;s1&quot;&gt;&amp;#39;data_*_backup.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;nb&quot;&gt;print&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, you print only the names of files that match the &lt;code&gt;data_*_backup.txt&lt;/code&gt; pattern. The asterisk in the pattern will match any character, so running this will find all text files whose filenames start with the word &lt;code&gt;data&lt;/code&gt; and end in &lt;code&gt;backup.txt&lt;/code&gt;, as you can see from the output below:&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;data_03_backup.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_02_backup.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_01_backup.txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;filename-pattern-matching-using-glob&quot;&gt;Filename Pattern Matching Using &lt;code&gt;glob&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Another useful module for pattern matching is &lt;code&gt;glob&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;.glob()&lt;/code&gt; in the &lt;code&gt;glob&lt;/code&gt; module works just like &lt;code&gt;fnmatch.fnmatch()&lt;/code&gt;, but unlike &lt;code&gt;fnmatch.fnmatch()&lt;/code&gt;, it treats files beginning with a period (&lt;code&gt;.&lt;/code&gt;) as special.&lt;/p&gt;
&lt;p&gt;UNIX and related systems translate name patterns with wildcards like &lt;code&gt;?&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt; into a list of files. This is called globbing.&lt;/p&gt;
&lt;p&gt;For example, typing &lt;code&gt;mv *.py python_files/&lt;/code&gt; in a UNIX shell moves (&lt;code&gt;mv&lt;/code&gt;) all files with the &lt;code&gt;.py&lt;/code&gt; extension from the current directory to the directory &lt;code&gt;python_files&lt;/code&gt;. The &lt;code&gt;*&lt;/code&gt; character is a wildcard that means &amp;ldquo;any number of characters,&amp;rdquo; and &lt;code&gt;*.py&lt;/code&gt; is the glob pattern. This shell capability is not available in the Windows Operating System. The &lt;code&gt;glob&lt;/code&gt; module adds this capability in Python, which enables Windows programs to use this feature.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of how to use &lt;code&gt;glob&lt;/code&gt; to search for all Python (&lt;code&gt;.py&lt;/code&gt;) source files in the current directory:&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;glob&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;*.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;admin.py&amp;#39;, &amp;#39;tests.py&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;glob.glob(&#39;*.py&#39;)&lt;/code&gt; searches for all files that have the &lt;code&gt;.py&lt;/code&gt; extension in the current directory and returns them as a list. &lt;code&gt;glob&lt;/code&gt; also supports shell-style wildcards to match patterns:&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;glob&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;*[0-9]*.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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This finds all text (&lt;code&gt;.txt&lt;/code&gt;) files that contain digits in the filename:&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;data_01.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_03.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_03_backup.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_02_backup.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_02.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;data_01_backup.txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;glob&lt;/code&gt; makes it easy to search for files recursively in subdirectories too:&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;glob&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;file&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iglob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;**/*.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recursive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &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;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This example makes use of &lt;code&gt;glob.iglob()&lt;/code&gt; to search for &lt;code&gt;.py&lt;/code&gt; files in the current directory and subdirectories. Passing &lt;code&gt;recursive=True&lt;/code&gt; as an argument to &lt;code&gt;.iglob()&lt;/code&gt; makes it search for &lt;code&gt;.py&lt;/code&gt; files in the current directory and any subdirectories. The difference between &lt;code&gt;glob.iglob()&lt;/code&gt; and &lt;code&gt;glob.glob()&lt;/code&gt; is that &lt;code&gt;.iglob()&lt;/code&gt; returns an iterator instead of a list.&lt;/p&gt;
&lt;p&gt;Running the program above produces 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;go&quot;&gt;admin.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;tests.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir/file1.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;sub_dir/file2.py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;pathlib&lt;/code&gt; contains similar methods for making flexible file listings. The example below shows how you can use &lt;code&gt;.Path.glob()&lt;/code&gt; to list file types that start with the letter &lt;code&gt;p&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;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;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;*.p*&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;admin.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;scraper.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;docs.pdf&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code&gt;p.glob(&#39;*.p*&#39;)&lt;/code&gt; returns a generator object that points to all files in the current directory that start with the letter &lt;code&gt;p&lt;/code&gt; in their file extension.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Path.glob()&lt;/code&gt; is similar to &lt;code&gt;os.glob()&lt;/code&gt; discussed above. As you can see, &lt;code&gt;pathlib&lt;/code&gt; combines many of the best features of the &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;os.path&lt;/code&gt;, and &lt;code&gt;glob&lt;/code&gt; modules into one single module, which makes it a joy to use.&lt;/p&gt;
&lt;p&gt;To recap, here is a table of the functions we have covered in this section:&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;Function&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;startswith()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tests if a string starts with a specified pattern and returns &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;endswith()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tests if a string ends with a specified pattern and returns &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fnmatch.fnmatch(filename, pattern)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tests whether the filename matches the pattern and returns &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;glob.glob()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns a list of filenames that match a pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pathlib.Path.glob()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Finds patterns in path names and returns a generator object&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&quot;traversing-directories-and-processing-files&quot;&gt;Traversing Directories and Processing Files&lt;/h2&gt;
&lt;p&gt;A common programming task is walking a directory tree and processing files in the tree. Let&amp;rsquo;s explore how the built-in Python function &lt;code&gt;os.walk()&lt;/code&gt; can be used to do this. &lt;code&gt;os.walk()&lt;/code&gt; is used to generate filename in a directory tree by walking the tree either top-down or bottom-up. For the purposes of this section, we&amp;rsquo;ll be manipulating the following directory tree:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.
|
├── folder_1/
|   ├── file1.py
|   ├── file2.py
|   └── file3.py
|
├── folder_2/
|   ├── file4.py
|   ├── file5.py
|   └── file6.py
|
├── test1.txt
└── test2.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The following is an example that shows you how to list all files and directories in a directory tree using &lt;code&gt;os.walk()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;os.walk()&lt;/code&gt; defaults to traversing directories in a top-down manner:&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;# Walking a directory tree and printing the names of the directories and files&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dirpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dirnames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;walk&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Found directory: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{dirpath}&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&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;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;os.walk()&lt;/code&gt; returns three values on each iteration of the loop:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The name of the current folder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A list of folders in the current folder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A list of files in the current folder&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On each iteration, it prints out the names of the subdirectories and files it finds:&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;Found directory: .&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;test1.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;test2.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Found directory: ./folder_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file1.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file3.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file2.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Found directory: ./folder_2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file4.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file5.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file6.py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To traverse the directory tree in a bottom-up manner, pass in a &lt;code&gt;topdown=False&lt;/code&gt; keyword argument to &lt;code&gt;os.walk()&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;dirpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dirnames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;walk&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;topdown&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;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Found directory: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{dirpath}&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&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;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Passing the &lt;code&gt;topdown=False&lt;/code&gt; argument will make &lt;code&gt;os.walk()&lt;/code&gt; print out the files it finds in the &lt;em&gt;subdirectories&lt;/em&gt; first:&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;Found directory: ./folder_1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file1.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file3.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file2.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Found directory: ./folder_2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file4.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file5.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;file6.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Found directory: .&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;test1.txt&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;test2.txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the program started by listing the contents of the subdirectories before listing the contents of the root directory. This is very useful in situations where you want to recursively delete files and directories. You will learn how to do this in the sections below. By default, &lt;code&gt;os.walk&lt;/code&gt; does not walk down into symbolic links that resolve to directories. This behavior can be overridden by calling it with a &lt;code&gt;followlinks=True&lt;/code&gt; argument.&lt;/p&gt;
&lt;h2 id=&quot;making-temporary-files-and-directories&quot;&gt;Making Temporary Files and Directories&lt;/h2&gt;
&lt;p&gt;Python provides a handy module for creating temporary files and directories called &lt;code&gt;tempfile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tempfile&lt;/code&gt; can be used to open and store data temporarily in a file or directory while your program is running. &lt;code&gt;tempfile&lt;/code&gt; handles the deletion of the temporary files when your program is done with them.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how to create a temporary 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;tempfile&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TemporaryFile&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Create a temporary file and write some data to it&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TemporaryFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;w+t&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello universe!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Go back to the beginning and read data from file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seek&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;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Close the file, after which it will be removed&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first step is to import &lt;code&gt;TemporaryFile&lt;/code&gt; from the &lt;code&gt;tempfile&lt;/code&gt; module. Next, create a file like object using the &lt;code&gt;TemporaryFile()&lt;/code&gt; method by calling it and passing the mode you want to open the file in. This will create and open a file that can be used as a temporary storage area.&lt;/p&gt;
&lt;p&gt;In the example above, the mode is &lt;code&gt;&#39;w+t&#39;&lt;/code&gt;, which makes &lt;code&gt;tempfile&lt;/code&gt; create a temporary text file in write mode. There is no need to give the temporary file a filename since it will be destroyed after the script is done running.&lt;/p&gt;
&lt;p&gt;After writing to the file, you can read from it and close it when you&amp;rsquo;re done processing it. Once the file is closed, it will be deleted from the filesystem. If you need to name the temporary files produced using &lt;code&gt;tempfile&lt;/code&gt;, use &lt;code&gt;tempfile.NamedTemporaryFile()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The temporary files and directories created using &lt;code&gt;tempfile&lt;/code&gt; are stored in a special system directory for storing temporary files. Python searches a standard list of directories to find one that the user can create files in.&lt;/p&gt;
&lt;p&gt;On Windows, the directories are &lt;code&gt;C:\TEMP&lt;/code&gt;, &lt;code&gt;C:\TMP&lt;/code&gt;, &lt;code&gt;\TEMP&lt;/code&gt;, and &lt;code&gt;\TMP&lt;/code&gt;, in that order. On all other platforms, the directories are &lt;code&gt;/tmp&lt;/code&gt;, &lt;code&gt;/var/tmp&lt;/code&gt;, and &lt;code&gt;/usr/tmp&lt;/code&gt;, in that order. As a last resort, &lt;code&gt;tempfile&lt;/code&gt; will save temporary files and directories in the current directory.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.TemporaryFile()&lt;/code&gt; is also a context manager so it can be used in conjunction with the &lt;code&gt;with&lt;/code&gt; statement. Using a context manager takes care of closing and deleting the file automatically after it has been 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;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TemporaryFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;w+t&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;fp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello universe!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seek&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;fp&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;# File is now closed and removed&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This creates a temporary file and reads data from it. As soon as the file&amp;rsquo;s contents are read, the temporary file is closed and deleted from the file system.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tempfile&lt;/code&gt; can also be used to create temporary directories. Let&amp;rsquo;s look at how you can do this using &lt;code&gt;tempfile.TemporaryDirectory()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tempfile&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TemporaryDirectory&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;tmpdir&lt;/span&gt;&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;Created temporary directory &amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmpdir&lt;/span&gt;&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;os&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmpdir&lt;/span&gt;&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;Created temporary directory  /tmp/tmpoxbkrm6c&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&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;# Directory contents have been removed&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmpdir&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;/tmp/tmpoxbkrm6c&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;os&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmpdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code&gt;tempfile.TemporaryDirectory()&lt;/code&gt; creates a temporary directory in the file system and returns an object representing this directory. In the example above, the directory is created using a context manager, and the name of the directory is stored in &lt;code&gt;tmpdir&lt;/code&gt;. The third line prints out the name of the temporary directory, and &lt;code&gt;os.path.exists(tmpdir)&lt;/code&gt; confirms if the directory was actually created in the file system.&lt;/p&gt;
&lt;p&gt;After the context manager goes out of context, the temporary directory is deleted and a call to &lt;code&gt;os.path.exists(tmpdir)&lt;/code&gt; returns &lt;code&gt;False&lt;/code&gt;, which means that the directory was succesfully deleted.&lt;/p&gt;
&lt;h2 id=&quot;deleting-files-and-directories&quot;&gt;Deleting Files and Directories&lt;/h2&gt;
&lt;p&gt;You can delete single files, directories, and entire directory trees using the methods found in the &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;shutil&lt;/code&gt;, and &lt;code&gt;pathlib&lt;/code&gt; modules. The following sections describe how to delete files and directories that you no longer need.&lt;/p&gt;
&lt;h3 id=&quot;deleting-files-in-python&quot;&gt;Deleting Files in Python&lt;/h3&gt;
&lt;p&gt;To delete a single file, use &lt;code&gt;pathlib.Path.unlink()&lt;/code&gt;, &lt;code&gt;os.remove()&lt;/code&gt;. or &lt;code&gt;os.unlink()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;os.remove()&lt;/code&gt; and &lt;code&gt;os.unlink()&lt;/code&gt; are semantically identical. To delete a file using &lt;code&gt;os.remove()&lt;/code&gt;, do the following:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;data_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;vuyisile&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Desktop&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;data.txt&amp;#39;&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;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Deleting a file using &lt;code&gt;os.unlink()&lt;/code&gt; is similar to how you do it using &lt;code&gt;os.remove()&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;os&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;data_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;vuyisile&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Desktop&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;data.txt&amp;#39;&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;unlink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code&gt;.unlink()&lt;/code&gt; or &lt;code&gt;.remove()&lt;/code&gt; on a file deletes the file from the filesystem. These two functions will throw an &lt;code&gt;OSError&lt;/code&gt; if the path passed to them points to a directory instead of a file. To avoid this, you can either check that what you&amp;rsquo;re trying to delete is actually a file and only delete it if it is, or you can use exception handling to handle the &lt;code&gt;OSError&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;os&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;data_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;home/data.txt&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# If the file exists, delete it&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&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;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_file&lt;/span&gt;&lt;span class=&quot;p&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;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Error: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{data_file}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; not a valid filename&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;os.path.isfile()&lt;/code&gt; checks whether &lt;code&gt;data_file&lt;/code&gt; is actually a file. If it is, it is deleted by the call to &lt;code&gt;os.remove()&lt;/code&gt;. If &lt;code&gt;data_file&lt;/code&gt; points to a folder, an error message is printed to the console.&lt;/p&gt;
&lt;p&gt;The following example shows how to use exception handling to handle errors when deleting files:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;data_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;home/data.txt&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Use exception handling&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;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_file&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;OSError&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Error: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{data_file}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; : &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{e.strerror}&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;The code above attempts to delete the file first before checking its type. If &lt;code&gt;data_file&lt;/code&gt; isn&amp;rsquo;t actually a file, the &lt;code&gt;OSError&lt;/code&gt; that is thrown is handled in the &lt;code&gt;except&lt;/code&gt; clause, and an error message is printed to the console. The error message that gets printed out is formatted using &lt;a href=&quot;https://realpython.com/python-f-strings/&quot;&gt;Python f-strings&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, you can also use &lt;code&gt;pathlib.Path.unlink()&lt;/code&gt; to delete files:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;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;span class=&quot;n&quot;&gt;data_file&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;s1&quot;&gt;&amp;#39;home/data.txt&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;data_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unlink&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;IsADirectoryError&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Error: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{data_file}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; : &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{e.strerror}&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;This creates a &lt;code&gt;Path&lt;/code&gt; object called &lt;code&gt;data_file&lt;/code&gt; that points to a file. Calling &lt;code&gt;.remove()&lt;/code&gt;  on &lt;code&gt;data_file&lt;/code&gt; will delete &lt;code&gt;home/data.txt&lt;/code&gt;. If &lt;code&gt;data_file&lt;/code&gt; points to a directory, an &lt;code&gt;IsADirectoryError&lt;/code&gt; is raised. It is worth noting that the Python program above has the same permissions as the user running it. If the user does not have permission to delete the file, a &lt;code&gt;PermissionError&lt;/code&gt; is raised.&lt;/p&gt;
&lt;h3 id=&quot;deleting-directories&quot;&gt;Deleting Directories&lt;/h3&gt;
&lt;p&gt;The standard library offers the following functions for deleting directories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;os.rmdir()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pathlib.Path.rmdir()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shutil.rmtree()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To delete a single directory or folder, use &lt;code&gt;os.rmdir()&lt;/code&gt; or &lt;code&gt;pathlib.rmdir()&lt;/code&gt;. These two functions only work if the directory you&amp;rsquo;re trying to delete is empty. If the directory isn&amp;rsquo;t empty, an &lt;code&gt;OSError&lt;/code&gt; is raised. Here is how to delete a folder:&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;n&quot;&gt;trash_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my_documents/bad_dir&amp;#39;&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;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rmdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trash_dir&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;OSError&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Error: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{trash_dir}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; : &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{e.strerror}&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;Here, the &lt;code&gt;trash_dir&lt;/code&gt; directory is deleted by passing its path to &lt;code&gt;os.rmdir()&lt;/code&gt;. If the directory isn&amp;rsquo;t empty, an error message is printed to the screen:&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;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;  File &amp;#39;&amp;lt;stdin&amp;gt;&amp;#39;, line 1, in &amp;lt;module&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;OSError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;[Errno 39] Directory not empty: &amp;#39;my_documents/bad_dir&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, you can use &lt;code&gt;pathlib&lt;/code&gt; to delete directories:&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;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;span class=&quot;n&quot;&gt;trash_dir&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;s1&quot;&gt;&amp;#39;my_documents/bad_dir&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;trash_dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rmdir&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;OSError&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Error: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{trash_dir}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; : &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{e.strerror}&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;Here, you create a &lt;code&gt;Path&lt;/code&gt; object that points to the directory to be deleted. Calling &lt;code&gt;.rmdir()&lt;/code&gt; on the &lt;code&gt;Path&lt;/code&gt; object will delete it if it is empty.&lt;/p&gt;
&lt;h3 id=&quot;deleting-entire-directory-trees&quot;&gt;Deleting Entire Directory Trees&lt;/h3&gt;
&lt;p&gt;To delete non-empty directories and entire directory trees, Python offers &lt;code&gt;shutil.rmtree()&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;shutil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;trash_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my_documents/bad_dir&amp;#39;&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;shutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rmtree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trash_dir&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;OSError&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Error: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{trash_dir}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; : &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{e.strerror}&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;Everything in &lt;code&gt;trash_dir&lt;/code&gt; is deleted when &lt;code&gt;shutil.rmtree()&lt;/code&gt; is called on it. There may be cases where you want to delete empty folders recursively. You can do this using one of the methods discussed above in conjunction with &lt;code&gt;os.walk()&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;os&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dirpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dirnames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&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;walk&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;topdown&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;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&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;rmdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dirpath&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;OSError&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&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;This walks down the directory tree and tries to delete each directory it finds. If the directory isn&amp;rsquo;t empty, an &lt;code&gt;OSError&lt;/code&gt; is raised and that directory is skipped. The table below lists the functions covered in this section:&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;Function&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;os.remove()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes a file and does not delete directories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;os.unlink()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Is identical to &lt;code&gt;os.remove()&lt;/code&gt; and deletes a single file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pathlib.Path.unlink()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes a file and cannot delete directories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;os.rmdir()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes an empty directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pathlib.Path.rmdir()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes an empty directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shutil.rmtree()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes entire directory tree and can be used to delete non-empty directories&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&quot;copying-moving-and-renaming-files-and-directories&quot;&gt;Copying, Moving, and Renaming Files and Directories&lt;/h2&gt;
&lt;p&gt;Python ships with the &lt;code&gt;shutil&lt;/code&gt; module. &lt;code&gt;shutil&lt;/code&gt; is short for shell utilities. It provides a number of high-level operations on files to support copying, archiving, and removal of files and directories. In this section, you&amp;rsquo;ll learn how to move and copy files and directories.&lt;/p&gt;
&lt;h3 id=&quot;copying-files-in-python&quot;&gt;Copying Files in Python&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;shutil&lt;/code&gt; offers a couple of functions for copying files. The most commonly used functions are &lt;code&gt;shutil.copy()&lt;/code&gt; and &lt;code&gt;shutil.copy2()&lt;/code&gt;. To copy a file from one location to another using &lt;code&gt;shutil.copy()&lt;/code&gt;, do the following:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;shutil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;path/to/file.txt&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;path/to/dest_dir&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;shutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dst&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;shutil.copy()&lt;/code&gt; is comparable to the &lt;code&gt;cp&lt;/code&gt; command in UNIX based systems. &lt;code&gt;shutil.copy(src, dst)&lt;/code&gt; will copy the file &lt;code&gt;src&lt;/code&gt; to the location specified in &lt;code&gt;dst&lt;/code&gt;. If &lt;code&gt;dst&lt;/code&gt; is a file, the contents of that file are replaced with the contents of &lt;code&gt;src&lt;/code&gt;. If &lt;code&gt;dst&lt;/code&gt; is a directory, then &lt;code&gt;src&lt;/code&gt; will be copied into that directory. &lt;code&gt;shutil.copy()&lt;/code&gt; only copies the file&amp;rsquo;s contents and the file&amp;rsquo;s permissions. Other metadata like the file&amp;rsquo;s creation and modification times are not preserved.&lt;/p&gt;
&lt;p&gt;To preserve all file metadata when copying, use &lt;code&gt;shutil.copy2()&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;shutil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;path/to/file.txt&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;path/to/dest_dir&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;shutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;copy2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;.copy2()&lt;/code&gt; preserves details about the file such as last access time, permission bits, last modification time, and flags.&lt;/p&gt;
&lt;h3 id=&quot;copying-directories&quot;&gt;Copying Directories&lt;/h3&gt;
&lt;p&gt;While &lt;code&gt;shutil.copy()&lt;/code&gt; only copies a single file, &lt;code&gt;shutil.copytree()&lt;/code&gt; will copy an entire directory and everything contained in it. &lt;code&gt;shutil.copytree(src, dest)&lt;/code&gt; takes two arguments: a source directory and the destination directory where files and folders will be copied to.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of how to copy the contents of one folder to a different location:&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;shutil&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;copytree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;data_1&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;data1_backup&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;data1_backup&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, &lt;code&gt;.copytree()&lt;/code&gt; copies the contents of &lt;code&gt;data_1&lt;/code&gt; to a new location &lt;code&gt;data1_backup&lt;/code&gt; and returns the destination directory. The destination directory must not already exist. It will be created as well as missing parent directories. &lt;code&gt;shutil.copytree()&lt;/code&gt; is a good way to back up your files.&lt;/p&gt;
&lt;h3 id=&quot;moving-files-and-directories&quot;&gt;Moving Files and Directories&lt;/h3&gt;
&lt;p&gt;To move a file or directory to another location, use &lt;code&gt;shutil.move(src, dst)&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;src&lt;/code&gt; is the file or directory to be moved and &lt;code&gt;dst&lt;/code&gt; is the destination:&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;shutil&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;dir_1/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;backup/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;backup&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;shutil.move(&#39;dir_1/&#39;, &#39;backup/&#39;)&lt;/code&gt; moves &lt;code&gt;dir_1/&lt;/code&gt; into &lt;code&gt;backup/&lt;/code&gt; if &lt;code&gt;backup/&lt;/code&gt; exists. If &lt;code&gt;backup/&lt;/code&gt; does not exist, &lt;code&gt;dir_1/&lt;/code&gt; will be renamed to &lt;code&gt;backup&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;renaming-files-and-directories&quot;&gt;Renaming Files and Directories&lt;/h3&gt;
&lt;p&gt;Python includes &lt;code&gt;os.rename(src, dst)&lt;/code&gt; for renaming files and directories:&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;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;first.zip&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;first_01.zip&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 line above will rename &lt;code&gt;first.zip&lt;/code&gt; to &lt;code&gt;first_01.zip&lt;/code&gt;. If the destination path points to a directory, it will raise an &lt;code&gt;OSError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another way to rename files or directories is to use &lt;code&gt;rename()&lt;/code&gt; from the &lt;code&gt;pathlib&lt;/code&gt; module:&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;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;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_file&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;s1&quot;&gt;&amp;#39;data_01.txt&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;data_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;data.txt&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To rename files using &lt;code&gt;pathlib&lt;/code&gt;, you first create a &lt;code&gt;pathlib.Path()&lt;/code&gt; object that contains a path to the file you want to replace. The next step is to call &lt;code&gt;rename()&lt;/code&gt; on the path object and pass a new filename for the file or directory you&amp;rsquo;re renaming.&lt;/p&gt;
&lt;h2 id=&quot;archiving&quot;&gt;Archiving&lt;/h2&gt;
&lt;p&gt;Archives are a convenient way to package several files into one. The two most common archive types are ZIP and TAR. The Python programs you write can create, read, and extract data from archives. You will learn how to read and write to both archive formats in this section.&lt;/p&gt;
&lt;h3 id=&quot;reading-zip-files&quot;&gt;Reading ZIP Files&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;zipfile&lt;/code&gt; module is a low level module that is part of the Python Standard Library. &lt;code&gt;zipfile&lt;/code&gt; has functions that make it easy to open and extract ZIP files. To read the contents of a ZIP file, the first thing to do is to create a &lt;code&gt;ZipFile&lt;/code&gt; object. &lt;code&gt;ZipFile&lt;/code&gt; objects are similar to file objects created using &lt;code&gt;open()&lt;/code&gt;. &lt;code&gt;ZipFile&lt;/code&gt; is also a context manager and therefore supports the &lt;code&gt;with&lt;/code&gt; statement:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;zipfile&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;data.zip&amp;#39;&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;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipobj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, you create a &lt;code&gt;ZipFile&lt;/code&gt; object, passing in the name of the ZIP file to open in read mode. After opening a ZIP file, information about the archive can be accessed through functions provided by the &lt;code&gt;zipfile&lt;/code&gt; module. The &lt;code&gt;data.zip&lt;/code&gt; archive in the example above was created from a directory named &lt;code&gt;data&lt;/code&gt; that contains a total of 5 files and 1 subdirectory:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.
|
├── sub_dir/
|   ├── bar.py
|   └── foo.py
|
├── file1.py
├── file2.py
└── file3.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To get a list of files in the archive, call &lt;code&gt;namelist()&lt;/code&gt; on the &lt;code&gt;ZipFile&lt;/code&gt; object:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;zipfile&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;data.zip&amp;#39;&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;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipobj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;zipobj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;namelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This produces a list:&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;[&amp;#39;file1.py&amp;#39;, &amp;#39;file2.py&amp;#39;, &amp;#39;file3.py&amp;#39;, &amp;#39;sub_dir/&amp;#39;, &amp;#39;sub_dir/bar.py&amp;#39;, &amp;#39;sub_dir/foo.py&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.namelist()&lt;/code&gt; returns a list of names of the files and directories in the archive. To retrieve information about the files in the archive, use &lt;code&gt;.getinfo()&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;zipfile&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;data.zip&amp;#39;&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;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipobj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bar_info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipobj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sub_dir/bar.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bar_info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_size&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s the output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;15277&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.getinfo()&lt;/code&gt; returns a &lt;code&gt;ZipInfo&lt;/code&gt; object that stores information about a single member of the archive. To get information about a file in the archive, you pass its path as an argument to &lt;code&gt;.getinfo()&lt;/code&gt;. Using &lt;code&gt;getinfo()&lt;/code&gt;, you&amp;rsquo;re able to retrieve information about archive members such as the date the files were last modified, their compressed sizes, and their full filenames. Accessing &lt;code&gt;.file_size&lt;/code&gt; retrieves the file&amp;rsquo;s original size in bytes.&lt;/p&gt;
&lt;p&gt;The following example shows how to retrieve more details about archived files in a Python REPL. Assume that the &lt;code&gt;zipfile&lt;/code&gt; module has been imported and &lt;code&gt;bar_info&lt;/code&gt; is the same object you created in previous examples:&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;bar_info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_time&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(2018, 10, 7, 23, 30, 10)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bar_info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress_size&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2856&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bar_info&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;go&quot;&gt;&amp;#39;sub_dir/bar.py&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;bar_info&lt;/code&gt; contains details about &lt;code&gt;bar.py&lt;/code&gt; such as its size when compressed and its full path.&lt;/p&gt;
&lt;p&gt;The first line shows how to retrieve a file&amp;rsquo;s last modified date. The next line shows how to get the size of the file after compression. The last line shows the full path of &lt;code&gt;bar.py&lt;/code&gt; in the archive.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ZipFile&lt;/code&gt; supports the context manager protocol, which is why you&amp;rsquo;re able to use it with the &lt;code&gt;with&lt;/code&gt; statement. Doing this automatically closes the &lt;code&gt;ZipFile&lt;/code&gt; object after you&amp;rsquo;re done with it. Trying to open or extract files from a closed &lt;code&gt;ZipFile&lt;/code&gt; object will result in an error.&lt;/p&gt;
&lt;h3 id=&quot;extracting-zip-archives&quot;&gt;Extracting ZIP Archives&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;zipfile&lt;/code&gt; module allows you to extract one or more files from ZIP archives through &lt;code&gt;.extract()&lt;/code&gt; and &lt;code&gt;.extractall()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These methods extract files to the current directory by default. They both take an optional &lt;code&gt;path&lt;/code&gt; parameter that allows you to specify a different directory to extract files to. If the directory does not exist, it is automatically created. To extract files from the archive, 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;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;zipfile&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;os&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;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;listdir&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;go&quot;&gt;[&amp;#39;data.zip&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;data_zip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;data.zip&amp;#39;&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Extract a single file to current directory&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_zip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;file1.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;/home/terra/test/dir1/zip_extract/file1.py&amp;#39;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listdir&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;go&quot;&gt;[&amp;#39;file1.py&amp;#39;, &amp;#39;data.zip&amp;#39;]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Extract all files into a different directory&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_zip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extractall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;extract_dir/&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;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listdir&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;go&quot;&gt;[&amp;#39;file1.py&amp;#39;, &amp;#39;extract_dir&amp;#39;, &amp;#39;data.zip&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;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;extract_dir&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;file1.py&amp;#39;, &amp;#39;file3.py&amp;#39;, &amp;#39;file2.py&amp;#39;, &amp;#39;sub_dir&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;data_zip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The third line of code is a call to &lt;code&gt;os.listdir()&lt;/code&gt;, which shows that the current directory has only one file, &lt;code&gt;data.zip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, you open &lt;code&gt;data.zip&lt;/code&gt; in read mode and call &lt;code&gt;.extract()&lt;/code&gt; to extract &lt;code&gt;file1.py&lt;/code&gt; from it. &lt;code&gt;.extract()&lt;/code&gt; returns the full file path of the extracted file. Since there&amp;rsquo;s no path specified, &lt;code&gt;.extract()&lt;/code&gt; extracts &lt;code&gt;file1.py&lt;/code&gt; to the current directory.&lt;/p&gt;
&lt;p&gt;The next line prints a directory listing showing that the current directory now includes the extracted file in addition to the original archive. The line after that shows how to extract the entire archive into the &lt;code&gt;zip_extract&lt;/code&gt; directory. &lt;code&gt;.extractall()&lt;/code&gt; creates the &lt;code&gt;extract_dir&lt;/code&gt; and extracts the contents of &lt;code&gt;data.zip&lt;/code&gt; into it. The last line closes the ZIP archive.&lt;/p&gt;
&lt;h3 id=&quot;extracting-data-from-password-protected-archives&quot;&gt;Extracting Data From Password Protected Archives&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;zipfile&lt;/code&gt; supports extracting password protected ZIPs. To extract password protected ZIP files, pass in the password to the &lt;code&gt;.extract()&lt;/code&gt; or &lt;code&gt;.extractall()&lt;/code&gt; method as an argument:&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;zipfile&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;secret.zip&amp;#39;&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;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pwd_zip&lt;/span&gt;&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;# Extract from a password protected archive&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;pwd_zip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extractall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;extract_dir&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Quish3@o&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 opens the &lt;code&gt;secret.zip&lt;/code&gt; archive in read mode. A password is supplied to &lt;code&gt;.extractall()&lt;/code&gt;, and the archive contents are extracted to &lt;code&gt;extract_dir&lt;/code&gt;. The archive is closed automatically after the extraction is complete thanks to the &lt;code&gt;with&lt;/code&gt; statement.&lt;/p&gt;
&lt;h3 id=&quot;creating-new-zip-archives&quot;&gt;Creating New ZIP Archives&lt;/h3&gt;
&lt;p&gt;To create a new ZIP archive, you open a &lt;code&gt;ZipFile&lt;/code&gt; object in write mode (&lt;code&gt;w&lt;/code&gt;) and add the files you want to archive:&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;zipfile&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_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;s1&quot;&gt;&amp;#39;file1.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sub_dir/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sub_dir/bar.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sub_dir/foo.py&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;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;new.zip&amp;#39;&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;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;new_zip&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;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the example, &lt;code&gt;new_zip&lt;/code&gt; is opened in write mode and each file in &lt;code&gt;file_list&lt;/code&gt; is added to the archive. When the &lt;code&gt;with&lt;/code&gt; statement suite is finished, &lt;code&gt;new_zip&lt;/code&gt; is closed. Opening a ZIP file in write mode erases the contents of the archive and creates a new archive.&lt;/p&gt;
&lt;p&gt;To add files to an existing archive, open a &lt;code&gt;ZipFile&lt;/code&gt; object in append mode and then add the files:&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;# Open a ZipFile object in append mode&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;new.zip&amp;#39;&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;new_zip&lt;/span&gt;&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;new_zip&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;s1&quot;&gt;&amp;#39;data.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;new_zip&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;s1&quot;&gt;&amp;#39;latin.txt&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, you open the &lt;code&gt;new.zip&lt;/code&gt; archive you created in the previous example in append mode. Opening the &lt;code&gt;ZipFile&lt;/code&gt; object in append mode allows you to add new files to the ZIP file without deleting its current contents. After adding files to the ZIP file, the &lt;code&gt;with&lt;/code&gt; statement goes out of context and closes the ZIP file.&lt;/p&gt;
&lt;h3 id=&quot;opening-tar-archives&quot;&gt;Opening TAR Archives&lt;/h3&gt;
&lt;p&gt;TAR files are uncompressed file archives like ZIP. They can be compressed using gzip, bzip2, and lzma compression methods. The &lt;code&gt;TarFile&lt;/code&gt; class allows reading and writing of TAR archives.&lt;/p&gt;
&lt;p&gt;Do this to read from an archive:&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;tarfile&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tarfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;example.tar&amp;#39;&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;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tar_file&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;tar_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getnames&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;tarfile&lt;/code&gt; objects open like most file-like objects. They have  an &lt;code&gt;open()&lt;/code&gt; function that takes a mode that determines how the file is to be opened.&lt;/p&gt;
&lt;p&gt;Use the &lt;code&gt;&#39;r&#39;&lt;/code&gt;, &lt;code&gt;&#39;w&#39;&lt;/code&gt; or &lt;code&gt;&#39;a&#39;&lt;/code&gt; modes to open an uncompressed TAR file for reading, writing, and appending, respectively. To open compressed TAR files, pass in a mode argument to &lt;code&gt;tarfile.open()&lt;/code&gt; that is in the form &lt;code&gt;filemode[:compression]&lt;/code&gt;. The table below lists the possible modes TAR files can be opened in:&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;Mode&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens archive for reading with transparent compression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r:gz&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens archive for reading with gzip compression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r:bz2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens archive for reading with bzip2 compression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r:xz&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens archive for reading with lzma compression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens archive for uncompressed writing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w:gz&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens archive for gzip compressed writing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w:xz&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens archive for lzma compressed writing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens archive for appending with no compression&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;.open()&lt;/code&gt; defaults to &lt;code&gt;&#39;r&#39;&lt;/code&gt; mode. To read an uncompressed TAR file and retrieve the names of the files in it, use &lt;code&gt;.getnames()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tarfile&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tarfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;example.tar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getnames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;CONTRIBUTING.rst&amp;#39;, &amp;#39;README.md&amp;#39;, &amp;#39;app.py&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This returns a list with the names of the archive contents.&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 the purposes of showing you how to use different &lt;code&gt;tarfile&lt;/code&gt; object methods, the TAR file in the examples is opened and closed manually in an interactive REPL session. &lt;/p&gt;
&lt;p&gt;Interacting with the TAR file this way allows you to see the output of running each command. Normally, you would want to use a context manager to open file-like objects.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The metadata of each entry in the archive can be accessed using special attributes:&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;entry&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getmembers&lt;/span&gt;&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;entry&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;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; Modified:&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mtime&lt;/span&gt;&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; Size    :&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bytes&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;go&quot;&gt;CONTRIBUTING.rst&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; Modified: Sat Nov  1 09:09:51 2018&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; Size    : 402 bytes&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;README.md&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; Modified: Sat Nov  3 07:29:40 2018&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; Size    : 5426 bytes&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;app.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; Modified: Sat Nov  3 07:29:13 2018&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; Size    : 6218 bytes&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, you loop through the list of files returned by &lt;code&gt;.getmembers()&lt;/code&gt; and print out each file&amp;rsquo;s attributes. The objects returned by &lt;code&gt;.getmembers()&lt;/code&gt; have attributes that can be accessed programmatically such as the name, size, and last modified time of each of the files in the archive. After reading or writing to the archive, it must be closed to free up system resources.&lt;/p&gt;
&lt;h3 id=&quot;extracting-files-from-a-tar-archive&quot;&gt;Extracting Files From a TAR Archive&lt;/h3&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll learn how to extract files from TAR archives using the following methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.extract()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.extractfile()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.extractall()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To extract a single file from a TAR archive, use &lt;code&gt;extract()&lt;/code&gt;, passing in the filename:&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;tar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;README.md&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;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listdir&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;go&quot;&gt;[&amp;#39;README.md&amp;#39;, &amp;#39;example.tar&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;README.md&lt;/code&gt; file is extracted from the archive to the file system. Calling &lt;code&gt;os.listdir()&lt;/code&gt; confirms that &lt;code&gt;README.md&lt;/code&gt; file was successfully extracted into the current directory. To unpack or extract everything from the archive, use &lt;code&gt;.extractall()&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;tar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extractall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;extracted/&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;.extractall()&lt;/code&gt; has an optional &lt;code&gt;path&lt;/code&gt; argument to specify where extracted files should go. Here, the archive is unpacked into the &lt;code&gt;extracted&lt;/code&gt; directory. The following commands show that the archive was successfully extracted:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls
&lt;span class=&quot;go&quot;&gt;example.tar  extracted  README.md&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; tree
&lt;span class=&quot;go&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;├── example.tar&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;├── extracted&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;|   ├── app.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;|   ├── CONTRIBUTING.rst&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;|   └── README.md&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;└── README.md&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;1 directory, 5 files&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls extracted/
&lt;span class=&quot;go&quot;&gt;app.py  CONTRIBUTING.rst  README.md&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To extract a file object for reading or writing, use &lt;code&gt;.extractfile()&lt;/code&gt;, which takes a filename or &lt;code&gt;TarInfo&lt;/code&gt; object to extract as an argument. &lt;code&gt;.extractfile()&lt;/code&gt; returns a file-like object that can be read and used:&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;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extractfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;app.py&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;f&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;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Opened archives should always be closed after they have been read or written to. To close an archive, call &lt;code&gt;.close()&lt;/code&gt; on the archive file handle or use the &lt;code&gt;with&lt;/code&gt; statement when creating &lt;code&gt;tarfile&lt;/code&gt; objects to automatically close the archive when you&amp;rsquo;re done. This frees up system resources and writes any changes you made to the archive to the filesystem.&lt;/p&gt;
&lt;h3 id=&quot;creating-new-tar-archives&quot;&gt;Creating New TAR Archives&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s how you do 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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tarfile&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_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;s1&quot;&gt;&amp;#39;app.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;config.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;CONTRIBUTORS.md&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;tests.py&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;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tarfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;packages.tar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&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;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;tar&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;file&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;c1&quot;&gt;# Read the contents of the newly created archive&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tarfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;package.tar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&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;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;member&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getmembers&lt;/span&gt;&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;member&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;go&quot;&gt;app.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;config.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;CONTRIBUTORS.md&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;tests.py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, you make a list of files to be added to the archive so that you don&amp;rsquo;t have to add each file manually.&lt;/p&gt;
&lt;p&gt;The next line uses the &lt;code&gt;with&lt;/code&gt; context manager to open a new archive called &lt;code&gt;packages.tar&lt;/code&gt; in write mode. Opening an archive in write mode(&lt;code&gt;&#39;w&#39;&lt;/code&gt;) enables you to write new files to the archive. Any existing files in the archive are deleted and a new archive is created.&lt;/p&gt;
&lt;p&gt;After the archive is created and populated, the &lt;code&gt;with&lt;/code&gt; context manager automatically closes it and saves it to the filesystem. The last three lines open the archive you just created and print out the names of the files contained in it.&lt;/p&gt;
&lt;p&gt;To add new files to an existing archive, open the archive in append mode (&lt;code&gt;&#39;a&#39;&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tarfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;package.tar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&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;tar&lt;/span&gt;&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;tar&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;s1&quot;&gt;&amp;#39;foo.bar&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;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tarfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;package.tar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&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;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;member&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getmembers&lt;/span&gt;&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;member&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;go&quot;&gt;app.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;config.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;CONTRIBUTORS.md&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;tests.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo.bar&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Opening an archive in append mode allows you to add new files to it without deleting the ones already in it.&lt;/p&gt;
&lt;h3 id=&quot;working-with-compressed-archives&quot;&gt;Working With Compressed Archives&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;tarfile&lt;/code&gt; can also read and write TAR archives compressed using gzip, bzip2, and lzma compression. To read or write to a compressed archive, use &lt;code&gt;tarfile.open()&lt;/code&gt;, passing in the appropriate mode for the compression type.&lt;/p&gt;
&lt;p&gt;For example, to read or write data to a TAR archive compressed using gzip, use the &lt;code&gt;&#39;r:gz&#39;&lt;/code&gt; or &lt;code&gt;&#39;w:gz&#39;&lt;/code&gt; modes respectively:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &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;app.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;config.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;tests.py&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;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tarfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;packages.tar.gz&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;w:gz&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;tar&lt;/span&gt;&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;tar&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;s1&quot;&gt;&amp;#39;app.py&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;tar&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;s1&quot;&gt;&amp;#39;config.py&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;tar&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;s1&quot;&gt;&amp;#39;tests.py&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;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tarfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;packages.tar.gz&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;r:gz&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;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;member&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getmembers&lt;/span&gt;&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;member&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;go&quot;&gt;app.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;config.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;tests.py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;&#39;w:gz&#39;&lt;/code&gt; mode opens the archive for gzip compressed writing and &lt;code&gt;&#39;r:gz&#39;&lt;/code&gt; opens the archive for gzip compressed reading.
Opening compressed archives in append mode is not possible. To add files to a compressed archive, you have to create a new archive.&lt;/p&gt;
&lt;h2 id=&quot;an-easier-way-of-creating-archives&quot;&gt;An Easier Way of Creating Archives&lt;/h2&gt;
&lt;p&gt;The Python Standard Library also supports creating TAR and ZIP archives using the high-level methods in the &lt;code&gt;shutil&lt;/code&gt; module.  The archiving utilities in &lt;code&gt;shutil&lt;/code&gt; allow you to create, read, and extract ZIP and TAR archives. These utilities rely on the lower level &lt;code&gt;tarfile&lt;/code&gt; and &lt;code&gt;zipfile&lt;/code&gt; modules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Working With Archives Using &lt;code&gt;shutil.make_archive()&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shutil.make_archive()&lt;/code&gt; takes at least two arguments: the name of the archive and an archive format.&lt;/p&gt;
&lt;p&gt;By default, it compresses all the files in the current directory into the archive format specified in the &lt;code&gt;format&lt;/code&gt; argument. You can pass in an optional &lt;code&gt;root_dir&lt;/code&gt; argument to compress files in a different directory.  &lt;code&gt;.make_archive()&lt;/code&gt; supports the &lt;code&gt;zip&lt;/code&gt;, &lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;bztar&lt;/code&gt;, and &lt;code&gt;gztar&lt;/code&gt; archive formats. &lt;/p&gt;
&lt;p&gt;This is how to create a TAR archive using &lt;code&gt;shutil&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;shutil&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# shutil.make_archive(base_name, format, root_dir)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;shutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make_archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;data/backup&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;tar&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;/pre&gt;&lt;/div&gt;

&lt;p&gt;This copies everything in &lt;code&gt;data/&lt;/code&gt; and creates an archive called &lt;code&gt;backup.tar&lt;/code&gt; in the filesystem and returns its name. To extract the archive, call &lt;code&gt;.unpack_archive()&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;shutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unpack_archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;backup.tar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;extract_dir/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code&gt;.unpack_archive()&lt;/code&gt; and passing in an archive name and destination directory extracts the contents of &lt;code&gt;backup.tar&lt;/code&gt; into &lt;code&gt;extract_dir/&lt;/code&gt;. ZIP archives can be created and extracted in the same way.&lt;/p&gt;
&lt;h2 id=&quot;reading-multiple-files&quot;&gt;Reading Multiple Files&lt;/h2&gt;
&lt;p&gt;Python supports reading data from multiple input streams or from a list of files through the &lt;code&gt;fileinput&lt;/code&gt; module. This module allows you to loop over the contents of one or more text files quickly and easily.
Here&amp;rsquo;s the typical way &lt;code&gt;fileinput&lt;/code&gt; is 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;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;fileinput&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;fileinput&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;fileinput&lt;/code&gt; gets its input from command line arguments passed to &lt;code&gt;sys.argv&lt;/code&gt; by default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;fileinput&lt;/code&gt; to Loop Over Multiple Files&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s use &lt;code&gt;fileinput&lt;/code&gt; to build a crude version of the common UNIX utility &lt;code&gt;cat&lt;/code&gt;. The &lt;code&gt;cat&lt;/code&gt; utility reads files sequentially, writing them to standard output. When given more than one file in its command line arguments, &lt;code&gt;cat&lt;/code&gt; will concatenate the text files and display the result in the terminal:&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;# File: fileinput-example.py&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;fileinput&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileinput&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&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;files&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;fileinput&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isfirstline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;--- Reading {fileinput.filename()} ---&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; -&amp;gt; &amp;#39;&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;end&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;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Running this on two text files in my current directory produces the following output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3 fileinput-example.py bacon.txt cupcake.txt
&lt;span class=&quot;go&quot;&gt;--- Reading bacon.txt ---&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; Spicy jalapeno bacon ipsum dolor amet in in aute est qui enim aliquip,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; irure cillum drumstick elit.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; Doner jowl shank ea exercitation landjaeger incididunt ut porchetta.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; Tenderloin bacon aliquip cupidatat chicken chuck quis anim et swine.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; Tri-tip doner kevin cillum ham veniam cow hamburger.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; Turkey pork loin cupidatat filet mignon capicola brisket cupim ad in.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; Ball tip dolor do magna laboris nisi pancetta nostrud doner.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;--- Reading cupcake.txt ---&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; Cupcake ipsum dolor sit amet candy I love cheesecake fruitcake.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; Topping muffin cotton candy.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; -&amp;gt; Gummies macaroon jujubes jelly beans marzipan.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;fileinput&lt;/code&gt; allows you to retrieve more information about each line such as whether or not it is the first line (&lt;code&gt;.isfirstline()&lt;/code&gt;), the line number (&lt;code&gt;.lineno()&lt;/code&gt;), and the filename (&lt;code&gt;.filename()&lt;/code&gt;). You can read more about it &lt;a href=&quot;https://docs.python.org/3/library/fileinput.html&quot;&gt;here&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 use Python to perform the most common operations on files and groups of files. You&amp;rsquo;ve learned about the different built-in modules used to read, find, and manipulate them.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;re now equipped to use Python to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get directory contents and file properties&lt;/li&gt;
&lt;li&gt;Create directories and directory trees&lt;/li&gt;
&lt;li&gt;Find patterns in filenames&lt;/li&gt;
&lt;li&gt;Create temporary files and directories&lt;/li&gt;
&lt;li&gt;Move, rename, copy, and delete files or directories&lt;/li&gt;
&lt;li&gt;Read and extract data from different types of archives&lt;/li&gt;
&lt;li&gt;Read multiple files simultaneously using &lt;code&gt;fileinput&lt;/code&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>Async IO in Python: A Complete Walkthrough</title>
      <id>https://realpython.com/async-io-python/</id>
      <link href="https://realpython.com/async-io-python/"/>
      <updated>2019-01-16T14:00:00+00:00</updated>
      <summary>This tutorial will give you a firm grasp of Python’s approach to async IO, which is a concurrent programming design that has received dedicated support in Python, evolving rapidly from Python 3.4 through 3.7 (and probably beyond).</summary>
      <content type="html">
        &lt;p&gt;Async IO is a concurrent programming design that has received dedicated support in Python, evolving rapidly from Python 3.4 through 3.7, and &lt;a href=&quot;https://twitter.com/1st1/status/1041855365745455104&quot;&gt;probably beyond&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You may be thinking with dread, &amp;ldquo;Concurrency, parallelism, threading, multiprocessing.  That&amp;rsquo;s a lot to grasp already.  Where does async IO fit in?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This tutorial is built to help you answer that question, giving you a firmer grasp of Python&amp;rsquo;s approach to async IO.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here&amp;rsquo;s what you&amp;rsquo;ll cover:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Asynchronous IO (async IO)&lt;/strong&gt;: a language-agnostic paradigm (model) that has implementations across a host of programming languages&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;&lt;/strong&gt;: two new Python keywords that are used to define coroutines&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;asyncio&lt;/code&gt;&lt;/strong&gt;: the Python package that provides a foundation and API for running and managing coroutines&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Coroutines (specialized generator functions) are the heart of async IO in Python, and we&amp;rsquo;ll dive into them later on.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In this article, I use the term &lt;strong&gt;async IO&lt;/strong&gt; to denote the language-agnostic design of asynchronous IO, while &lt;code&gt;asyncio&lt;/code&gt; refers to the Python package.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Before you get started, you&amp;rsquo;ll need to make sure you&amp;rsquo;re set up to use &lt;code&gt;asyncio&lt;/code&gt; and other libraries found in this tutorial.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;setting-up-your-environment&quot;&gt;Setting Up Your Environment&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ll need Python 3.7 or above to follow this article in its entirety, as well as the &lt;code&gt;aiohttp&lt;/code&gt; and &lt;code&gt;aiofiles&lt;/code&gt; packages:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3.7 -m venv ./py37async
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; ./py37async/bin/activate  &lt;span class=&quot;c1&quot;&gt;# Windows: .\py37async\Scripts\activate.bat&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install --upgrade pip aiohttp aiofiles  &lt;span class=&quot;c1&quot;&gt;# Optional: aiodns&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;One&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Two&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{__file__}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; executed in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{elapsed:0.2f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you execute this file, take note of what looks different than if you were to define the functions with just &lt;code&gt;def&lt;/code&gt; and &lt;code&gt;time.sleep()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3 countasync.py
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;countasync.py executed in 1.01 seconds.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The order of this output is the heart of async IO.  Talking to each of the calls to &lt;code&gt;count()&lt;/code&gt; is a single event loop, or coordinator.  When each task reaches &lt;code&gt;await asyncio.sleep(1)&lt;/code&gt;, the function yells up to the event loop and gives control back to it, saying, &amp;ldquo;I&amp;rsquo;m going to be sleeping for 1 second.  Go ahead and let something else meaningful be done in the meantime.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Contrast this to the synchronous version:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# countsync.py&lt;/span&gt;

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

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;One&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Two&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{__file__}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; executed in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{elapsed:0.2f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When executed, there is a slight but critical change in order and execution time:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3 countsync.py
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;One&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Two&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;countsync.py executed in 3.01 seconds.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# OK - this is an async generator&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# No - SyntaxError&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Still no - SyntaxError (no `async def` here)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;span class=&quot;nd&quot;&gt;@asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coroutine&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;py34_coro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Generator-based coroutine, older syntax&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;py35_coro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Native coroutine, modern syntax&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;span class=&quot;c1&quot;&gt;# ANSI colors&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[0m&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# End of color&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[36m&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Cyan&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[91m&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Red&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[35m&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Magenta&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;makerandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Initiated makerandom(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{idx}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;).&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;makerandom(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{idx}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) == &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; too low; retrying.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;---&amp;gt; Finished: makerandom(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{idx}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) == &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makerandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;444&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;r1: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{r1}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, r2: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{r2}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, r3: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{r3}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The colorized output says a lot more than I can and gives you a sense for how this script is carried out:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/asyncio-rand.dffdd83b4256.gif&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-70&quot; src=&quot;https://files.realpython.com/media/asyncio-rand.dffdd83b4256.gif&quot; width=&quot;300&quot; height=&quot;400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/asyncio-rand.dffdd83b4256.gif&amp;amp;w=75&amp;amp;sig=1730fc7b8bca67960626d95ccf73c50dfe4c66dd 75w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/asyncio-rand.dffdd83b4256.gif&amp;amp;w=150&amp;amp;sig=35259ed0d3ddd6b7d3165c31498b67d657715b21 150w, https://files.realpython.com/media/asyncio-rand.dffdd83b4256.gif 300w&quot; sizes=&quot;75vw&quot; alt=&quot;rand.py program execution&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;rand.py execution&lt;/figcaption&gt;&lt;/figure&gt;

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

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

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;part1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;part1(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) sleeping for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;result&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-1&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Returning part1(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) == &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{result}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;part2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;part2{n, arg} sleeping for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;result&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-2 derived from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{arg}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Returning part2{n, arg} == &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{result}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;--&amp;gt;Chained result&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; =&amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{p2}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (took &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{end:0.2f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds).&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;444&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Program finished in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{end:0.2f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;itertools&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;it&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;makeitem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;randsleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;caller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;caller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{caller}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; sleeping for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;produce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Synchronous loop for each single producer&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randsleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;makeitem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; added &amp;lt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;gt; to queue.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randsleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; got element &amp;lt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{i}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;gt;&amp;quot;&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; in {now-t:0.5f} seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task_done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nprod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ncon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;producers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;produce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nprod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;consumers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ncon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Implicitly awaits consumers, too&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consumers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;argparse&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;444&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argparse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;-p&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;--nprod&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;-c&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;--ncon&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__dict__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perf_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Program completed in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{elapsed:0.5f}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;span class=&quot;nd&quot;&gt;@asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coroutine&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;py34_coro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Generator-based coroutine&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# No need to build these yourself, but be aware of what they are&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;py35_coro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Native coroutine, modern syntax&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x30&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As an experiment, what happens if you call &lt;code&gt;py34_coro()&lt;/code&gt; or &lt;code&gt;py35_coro()&lt;/code&gt; on its own, without &lt;code&gt;await&lt;/code&gt;, or without any calls to &lt;code&gt;asyncio.run()&lt;/code&gt; or other &lt;code&gt;asyncio&lt;/code&gt; &amp;ldquo;porcelain&amp;rdquo; functions?  Calling a coroutine in isolation returns a coroutine object:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;py35_coro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;coroutine object py35_coro at 0x10126dcc8&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;Generator functions are, as it so happens, the foundation of async IO (regardless of whether you declare coroutines with &lt;code&gt;async def&lt;/code&gt; rather than the older &lt;code&gt;@asyncio.coroutine&lt;/code&gt; wrapper).  Technically, &lt;code&gt;await&lt;/code&gt; is more closely analogous to &lt;code&gt;yield from&lt;/code&gt; than it is to &lt;code&gt;yield&lt;/code&gt;.  (But remember that &lt;code&gt;yield from x()&lt;/code&gt; is just syntactic sugar to replace &lt;code&gt;for i in x(): yield i&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;One critical feature of generators as it pertains to async IO is that they can effectively be stopped and restarted at will.  For example, you can &lt;code&gt;break&lt;/code&gt; out of iterating over a generator object and then resume iteration on the remaining values later.  When a generator function reaches &lt;code&gt;yield&lt;/code&gt;, it yields that value, but then it sits idle until it is told to yield its subsequent value.&lt;/p&gt;
&lt;p&gt;This can be fleshed out through an example:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;itertools&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cycle&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;endless&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Yields 9, 8, 7, 6, 9, 8, 7, 6, ... forever&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;yield from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cycle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endless&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# Pause execution. We can resume later.&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;9 8 7 6 9 8 7 6 9 8 7 6 9 8&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Resume&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(6, 9, 8)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;Last but not least, Python enables &lt;strong&gt;asynchronous comprehension&lt;/strong&gt; with &lt;code&gt;async for&lt;/code&gt;.  Like its synchronous cousin, this is largely syntactic sugar:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# This does *not* introduce concurrent execution&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# It is meant to show syntax only&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mygen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mygen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 2, 16, 32, 256, 512]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;&lt;a href=&quot;https://github.com/python/cpython/blob/d4c76d960b8b286b75c933780416ace9cda682fd/Lib/asyncio/runners.py#L8&quot;&gt;&lt;code&gt;asyncio.run()&lt;/code&gt;&lt;/a&gt;, introduced in Python 3.7, is responsible for getting the event loop, running tasks until they are marked as complete, and then closing the event loop.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a more long-winded way of managing the &lt;code&gt;asyncio&lt;/code&gt; event loop, with &lt;code&gt;get_event_loop()&lt;/code&gt;.  The typical pattern looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_event_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run_until_complete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ll probably see &lt;code&gt;loop.get_event_loop()&lt;/code&gt; floating around in older examples, but unless you have a specific need to fine-tune control over the event loop management, &lt;code&gt;asyncio.run()&lt;/code&gt; should be sufficient for most programs.&lt;/p&gt;
&lt;p&gt;If you do need to interact with the event loop within a Python program, &lt;code&gt;loop&lt;/code&gt; is a good-old-fashioned Python object that supports introspection with &lt;code&gt;loop.is_running()&lt;/code&gt; and &lt;code&gt;loop.is_closed()&lt;/code&gt;.  You can manipulate it if you need to get more fine-tuned control, such as in &lt;a href=&quot;https://docs.python.org/3/library/asyncio-eventloop.html#asyncio-example-lowlevel-helloworld&quot;&gt;scheduling a callback&lt;/a&gt; by passing the loop as an argument.&lt;/p&gt;
&lt;p&gt;What is more crucial is understanding a bit beneath the surface about the mechanics of the event loop.  Here are a few points worth stressing about the event loop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;#1:&lt;/strong&gt; Coroutines don&amp;rsquo;t do much on their own until they are tied to the event loop.&lt;/p&gt;
&lt;p&gt;You saw this point before in the explanation on generators, but it&amp;rsquo;s worth restating.  If you have a main coroutine that awaits others, simply calling it in isolation has little effect:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Hello ...&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;World!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;routine&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;routine&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;coroutine object main at 0x1027a6150&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Remember to use &lt;code&gt;asyncio.run()&lt;/code&gt; to actually force execution by scheduling the &lt;code&gt;main()&lt;/code&gt; coroutine (future object) for execution on the event loop:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;routine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Hello ...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;World!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;logging&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;re&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IO&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.error&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.parse&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;aiofiles&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;aiohttp&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;aiohttp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientSession&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(levelname)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(name)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;areq&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;chardet.charsetprober&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;disabled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;HREF_RE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;href=&amp;quot;(.*?)&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch_html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;GET request wrapper to fetch page HTML.&lt;/span&gt;

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

    &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raise_for_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Got response [&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;] for URL: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;html&lt;/span&gt;

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

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;write_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Write the found HREFs from `url` to `file`.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aiofiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{url}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{p}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Wrote results for source URL: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bulk_crawl_and_write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Crawl &amp;amp; write concurrently to `file` for multiple `urls`.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;write_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pathlib&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version_info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Script requires Python 3.7+.&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;here&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathlib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__file__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;here&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;joinpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;urls.txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;outpath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;here&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;joinpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;foundurls.txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;outfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;source_url&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;parsed_url&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bulk_crawl_and_write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This script is longer than our initial toy programs, so let&amp;rsquo;s break it down.&lt;/p&gt;
&lt;p&gt;The constant &lt;code&gt;HREF_RE&lt;/code&gt; is a regular expression to extract what we&amp;rsquo;re ultimately searching for, &lt;code&gt;href&lt;/code&gt; tags within HTML:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HREF_RE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Go to &amp;lt;a href=&amp;quot;https://realpython.com/&amp;quot;&amp;gt;Real Python&amp;lt;/a&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;re.Match object; span=(15, 45), match=&amp;#39;href=&amp;quot;https://realpython.com/&amp;quot;&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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

&lt;p&gt;That&amp;rsquo;s not too shabby!  As a sanity check, you can check the line-count on the output.  In my case, it&amp;rsquo;s 626, though keep in mind this may fluctuate:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; wc -l foundurls.txt
&lt;span class=&quot;go&quot;&gt;     626 foundurls.txt&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; head -n &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; foundurls.txt
&lt;span class=&quot;go&quot;&gt;source_url  parsed_url&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://www.bloomberg.com/markets/economics https://www.bloomberg.com/feedback&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;https://www.bloomberg.com/markets/economics https://www.bloomberg.com/notices/tos&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;coro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&amp;#39;IO&amp;#39; wait time is proportional to the max element.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;reversed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# This is a bit redundant in the case of one task&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# We could use `await coro([3, 2, 1])` on its own&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Python 3.7+&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;t: type {type(t)}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;t done: {t.done()}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;t: type &amp;lt;class &amp;#39;_asyncio.Task&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;t done: True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;p&gt;Lastly, you may also see &lt;code&gt;asyncio.ensure_future()&lt;/code&gt;.  You should rarely need it, because it&amp;rsquo;s a lower-level plumbing API and largely replaced by &lt;code&gt;create_task()&lt;/code&gt;, which was introduced later.&lt;/p&gt;
&lt;h3 id=&quot;the-precedence-of-await&quot;&gt;The Precedence of &lt;code&gt;await&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;While they behave somewhat similarly, the &lt;code&gt;await&lt;/code&gt; keyword has significantly higher precedence than &lt;code&gt;yield&lt;/code&gt;.  This means that, because it is more tightly bound, there are a number of instances where you&amp;rsquo;d need parentheses in a &lt;code&gt;yield from&lt;/code&gt; statement that are not required in an analogous &lt;code&gt;await&lt;/code&gt; statement.  For more information, see &lt;a href=&quot;https://www.python.org/dev/peps/pep-0492/#examples-of-await-expressions&quot;&gt;examples of &lt;code&gt;await&lt;/code&gt; expressions&lt;/a&gt; from PEP 492.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;re now equipped to use &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; and the libraries built off of it.  Here&amp;rsquo;s a recap of what you&amp;rsquo;ve covered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Asynchronous IO as a language-agnostic model and a way to effect concurrency by letting coroutines indirectly communicate with each other&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The specifics of Python&amp;rsquo;s new &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; keywords, used to mark and define coroutines&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;asyncio&lt;/code&gt;, the Python package that provides the API to run and manage coroutines&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;
&lt;h3 id=&quot;python-version-specifics&quot;&gt;Python Version Specifics&lt;/h3&gt;
&lt;p&gt;Async IO in Python has evolved swiftly, and it can be hard to keep track of what came when.  Here&amp;rsquo;s a list of Python minor-version changes and introductions related to &lt;code&gt;asyncio&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;3.3: The &lt;code&gt;yield from&lt;/code&gt; expression allows for generator delegation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.4: &lt;code&gt;asyncio&lt;/code&gt; was introduced in the Python standard library with provisional API status.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.5: &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; became a part of the Python grammar, used to signify and wait on coroutines.  They were not yet reserved keywords.  (You could still define functions or variables named &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.6: Asynchronous generators and asynchronous comprehensions were introduced.  The API of &lt;code&gt;asyncio&lt;/code&gt; was declared stable rather than provisional.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.7: &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; became reserved keywords.  (They cannot be used as identifiers.)  They are intended to replace the &lt;code&gt;asyncio.coroutine()&lt;/code&gt; decorator.  &lt;code&gt;asyncio.run()&lt;/code&gt; was introduced to the &lt;code&gt;asyncio&lt;/code&gt; package, among &lt;a href=&quot;https://docs.python.org/3/whatsnew/3.7.html#whatsnew37-asyncio&quot;&gt;a bunch of other features&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to be safe (and be able to use &lt;code&gt;asyncio.run()&lt;/code&gt;), go with Python 3.7 or above to get the full set of features.&lt;/p&gt;
&lt;h3 id=&quot;articles&quot;&gt;Articles&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s a curated list of additional resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Real Python: &lt;a href=&quot;https://realpython.com/python-concurrency/&quot;&gt;Speed up your Python Program with Concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Real Python: &lt;a href=&quot;https://realpython.com/python-gil/&quot;&gt;What is the Python Global Interpreter Lock?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CPython: The &lt;code&gt;asyncio&lt;/code&gt; package &lt;a href=&quot;https://github.com/python/cpython/tree/master/Lib/asyncio&quot;&gt;source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Python docs: &lt;a href=&quot;https://docs.python.org/3/reference/datamodel.html#coroutines&quot;&gt;Data model &amp;gt; Coroutines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;TalkPython: &lt;a href=&quot;https://training.talkpython.fm/courses/details/async-in-python-with-threading-and-multiprocessing&quot;&gt;Async Techniques and Examples in Python&lt;/a&gt; &lt;a href=&quot;https://github.com/talkpython/async-techniques-python-course&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Brett Cannon: &lt;a href=&quot;https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/&quot;&gt;How the Heck Does Async-Await Work in Python 3.5?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PYMOTW: &lt;a href=&quot;https://pymotw.com/3/asyncio/&quot;&gt;&lt;code&gt;asyncio&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A. Jesse Jiryu Davis and Guido van Rossum: &lt;a href=&quot;http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html&quot;&gt;A Web Crawler With asyncio Coroutines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Andy Pearce: &lt;a href=&quot;http://www.andy-pearce.com/blog/posts/2016/Jun/the-state-of-python-coroutines-yield-from/&quot;&gt;The State of Python Coroutines: &lt;code&gt;yield from&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Nathaniel J. Smith: &lt;a href=&quot;https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/&quot;&gt;Some Thoughts on Asynchronous API Design in a Post-&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; World&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Armin Ronacher: &lt;a href=&quot;http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/&quot;&gt;I don&amp;rsquo;t understand Python&amp;rsquo;s Asyncio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Andy Balaam: &lt;a href=&quot;http://www.artificialworlds.net/blog/2017/05/31/basic-ideas-of-python-3-asyncio-concurrency/&quot;&gt;series on &lt;code&gt;asyncio&lt;/code&gt;&lt;/a&gt; (4 posts)&lt;/li&gt;
&lt;li&gt;Stack Overflow: &lt;a href=&quot;https://stackoverflow.com/q/40836800/7954504&quot;&gt;Python &lt;code&gt;asyncio.semaphore&lt;/code&gt; in &lt;code&gt;async&lt;/code&gt;-&lt;code&gt;await&lt;/code&gt; function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Yeray Diaz:&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hackernoon.com/asyncio-for-the-working-python-developer-5c468e6e2e8e&quot;&gt;AsyncIO for the Working Python Developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/python-pandemonium/asyncio-coroutine-patterns-beyond-await-a6121486656f&quot;&gt;Asyncio Coroutine Patterns: Beyond &lt;code&gt;await&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few Python &lt;em&gt;What&amp;rsquo;s New&lt;/em&gt; sections explain the motivation behind language changes in more detail:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/whatsnew/3.3.html#pep-380&quot;&gt;What&amp;rsquo;s New in Python 3.3&lt;/a&gt; (&lt;code&gt;yield from&lt;/code&gt; and PEP 380)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep525&quot;&gt;What&amp;rsquo;s New in Python 3.6&lt;/a&gt; (PEP 525 &amp;amp; 530)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From David Beazley:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dabeaz.com/generators/&quot;&gt;Generator: Tricks for Systems Programmers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dabeaz.com/coroutines/&quot;&gt;A Curious Course on Coroutines and Concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://dabeaz.com/finalgenerator/index.html&quot;&gt;Generators: The Final Frontier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;YouTube talks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/0kXaLh8Fz3k&quot;&gt;John Reese - Thinking Outside the GIL with AsyncIO and Multiprocessing - PyCon 2018&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/ZzfHjytDceU&quot;&gt;Keynote David Beazley - Topics of Interest (Python Asyncio)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/MCs5OvhV9S4&quot;&gt;David Beazley - Python Concurrency From the Ground Up: LIVE! - PyCon 2015&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/9zinZmE3Ogk&quot;&gt;Raymond Hettinger, Keynote on Concurrency, PyBay 2017&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/Bv25Dwe84g0&quot;&gt;Thinking about Concurrency, Raymond Hettinger, Python core developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/iG6fr81xHKA&quot;&gt;Miguel Grinberg Asynchronous Python for the Complete Beginner PyCon 2017&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/2ZFFv-wZ8_g&quot;&gt;Yury Selivanov asyncawait and asyncio in Python 3 6 and beyond PyCon 2017&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/E-1Y4kSsAFc&quot;&gt;Fear and Awaiting in Async: A Savage Journey to the Heart of the Coroutine Dream&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/kdzL3r-yJZY&quot;&gt;What Is Async, How Does It Work, and When Should I Use It? (PyCon APAC 2014)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;related-peps&quot;&gt;Related PEPs&lt;/h3&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PEP&lt;/th&gt;
&lt;th&gt;Date Created&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0342/&quot;&gt;PEP 342 &amp;ndash; Coroutines via Enhanced Generators&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2005-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0380/&quot;&gt;PEP 380 &amp;ndash; Syntax for Delegating to a Subgenerator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2009-02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-3153/&quot;&gt;PEP 3153 &amp;ndash; Asynchronous IO support&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2011-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-3156/&quot;&gt;PEP 3156 &amp;ndash; Asynchronous IO Support Rebooted: the &amp;ldquo;asyncio&amp;rdquo; Module&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2012-12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0492/&quot;&gt;PEP 492 &amp;ndash; Coroutines with async and await syntax&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2015-04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0525/&quot;&gt;PEP 525 &amp;ndash; Asynchronous Generators&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2016-07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0530/&quot;&gt;PEP 530 &amp;ndash; Asynchronous Comprehensions&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2016-09&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;libraries-that-work-with-asyncawait&quot;&gt;Libraries That Work With &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;From &lt;a href=&quot;https://github.com/aio-libs&quot;&gt;aio-libs&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiohttp&quot;&gt;&lt;code&gt;aiohttp&lt;/code&gt;&lt;/a&gt;: Asynchronous HTTP client/server framework&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aioredis&quot;&gt;&lt;code&gt;aioredis&lt;/code&gt;&lt;/a&gt;: Async IO Redis support&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiopg&quot;&gt;&lt;code&gt;aiopg&lt;/code&gt;&lt;/a&gt;: Async IO PostgreSQL support&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiomcache&quot;&gt;&lt;code&gt;aiomcache&lt;/code&gt;&lt;/a&gt;: Async IO memcached client&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiokafka&quot;&gt;&lt;code&gt;aiokafka&lt;/code&gt;&lt;/a&gt;: Async IO Kafka client&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiozmq&quot;&gt;&lt;code&gt;aiozmq&lt;/code&gt;&lt;/a&gt;: Async IO ZeroMQ support&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/aiojobs&quot;&gt;&lt;code&gt;aiojobs&lt;/code&gt;&lt;/a&gt;: Jobs scheduler for managing background tasks&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aio-libs/async_lru&quot;&gt;&lt;code&gt;async_lru&lt;/code&gt;&lt;/a&gt;: Simple LRU cache for async IO&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From &lt;a href=&quot;https://magic.io/&quot;&gt;magicstack&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/MagicStack/uvloop&quot;&gt;&lt;code&gt;uvloop&lt;/code&gt;&lt;/a&gt;: Ultra fast async IO event loop&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/MagicStack/asyncpg&quot;&gt;&lt;code&gt;asyncpg&lt;/code&gt;&lt;/a&gt;: (Also very fast) async IO PostgreSQL support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From other hosts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/python-trio/trio&quot;&gt;&lt;code&gt;trio&lt;/code&gt;&lt;/a&gt;: Friendlier &lt;code&gt;asyncio&lt;/code&gt; intended to showcase a radically simpler design&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Tinche/aiofiles&quot;&gt;&lt;code&gt;aiofiles&lt;/code&gt;&lt;/a&gt;: Async file IO&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/theelous3/asks&quot;&gt;&lt;code&gt;asks&lt;/code&gt;&lt;/a&gt;: Async requests-like http library&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jonathanslenders/asyncio-redis&quot;&gt;&lt;code&gt;asyncio-redis&lt;/code&gt;&lt;/a&gt;: Async IO Redis support&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dano/aioprocessing&quot;&gt;&lt;code&gt;aioprocessing&lt;/code&gt;&lt;/a&gt;: Integrates &lt;code&gt;multiprocessing&lt;/code&gt; module with &lt;code&gt;asyncio&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Scille/umongo&quot;&gt;&lt;code&gt;umongo&lt;/code&gt;&lt;/a&gt;: Async IO MongoDB client&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/alex-sherman/unsync&quot;&gt;&lt;code&gt;unsync&lt;/code&gt;&lt;/a&gt;: Unsynchronize &lt;code&gt;asyncio&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vxgmichel/aiostream&quot;&gt;&lt;code&gt;aiostream&lt;/code&gt;&lt;/a&gt;: Like &lt;code&gt;itertools&lt;/code&gt;, but async&lt;/li&gt;
&lt;/ul&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Speed Up Your Python Program With Concurrency</title>
      <id>https://realpython.com/python-concurrency/</id>
      <link href="https://realpython.com/python-concurrency/"/>
      <updated>2019-01-14T14:00:00+00:00</updated>
      <summary>Learn what concurrency means in Python and why you might want to use it. You&#39;ll see a simple, non-concurrent approach and then look into why you&#39;d want threading, asyncio, or multiprocessing.</summary>
      <content type="html">
        &lt;p&gt;If you&amp;rsquo;ve heard lots of talk about &lt;code&gt;asyncio&lt;/code&gt; &lt;a href=&quot;https://realpython.com/python37-new-features/&quot;&gt;being added to Python&lt;/a&gt; but are curious how it compares to other concurrency methods or are wondering what concurrency is and how it might speed up your program, you&amp;rsquo;ve come to the right place.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn the following:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What &lt;strong&gt;concurrency&lt;/strong&gt; is&lt;/li&gt;
&lt;li&gt;What &lt;strong&gt;parallelism&lt;/strong&gt; is&lt;/li&gt;
&lt;li&gt;How some of &lt;strong&gt;Python&amp;rsquo;s concurrency methods&lt;/strong&gt; compare, including &lt;code&gt;threading&lt;/code&gt;, &lt;code&gt;asyncio&lt;/code&gt;, and &lt;code&gt;multiprocessing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When to use concurrency&lt;/strong&gt; in your program and which module to use&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article assumes that you have a basic understanding of Python and that you&amp;rsquo;re using at least version 3.6 to run the examples. You can download the examples from the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/concurrency-overview&quot;&gt;&lt;em&gt;Real Python&lt;/em&gt; GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

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

&lt;h2 id=&quot;what-is-concurrency&quot;&gt;What Is Concurrency?&lt;/h2&gt;
&lt;p&gt;The dictionary definition of concurrency is simultaneous occurrence. In Python, the things that are occurring simultaneously are called by different names (thread, task, process) but at a high level, they all refer to a sequence of instructions that run in order.&lt;/p&gt;
&lt;p&gt;I like to think of them as different trains of thought. Each one can be stopped at certain points, and the CPU or brain that is processing them can switch to a different one. The state of each one is saved so it can be restarted right where it was interrupted.&lt;/p&gt;
&lt;p&gt;You might wonder why Python uses different words for the same concept. It turns out that threads, tasks, and processes are only the same if you view them from a high level. Once you start digging into the details, they all represent slightly different things. You&amp;rsquo;ll see more of how they are different as you progress through the examples.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s talk about the simultaneous part of that definition. You have to be a little careful because, when you get down to the details, only &lt;code&gt;multiprocessing&lt;/code&gt; actually runs these trains of thought at literally the same time. &lt;code&gt;Threading&lt;/code&gt; and &lt;code&gt;asyncio&lt;/code&gt; both run on a single processor and therefore only run one at a time. They just cleverly find ways to take turns to speed up the overall process. Even though they don&amp;rsquo;t run different trains of thought simultaneously, we still call this concurrency.&lt;/p&gt;
&lt;p&gt;The way the threads or tasks take turns is the big difference between &lt;code&gt;threading&lt;/code&gt; and &lt;code&gt;asyncio&lt;/code&gt;. In &lt;code&gt;threading&lt;/code&gt;, the operating system actually knows about each thread and can interrupt it at any time to start running a different thread. This is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Preemption_%28computing%29#Preemptive_multitasking&quot;&gt;pre-emptive multitasking&lt;/a&gt; since the operating system can pre-empt your thread to make the switch.&lt;/p&gt;
&lt;p&gt;Pre-emptive multitasking is handy in that the code in the thread doesn&amp;rsquo;t need to do anything to make the switch. It can also be difficult because of that &amp;ldquo;at any time&amp;rdquo; phrase. This switch can happen in the middle of a single Python statement, even a trivial one like &lt;code&gt;x = x + 1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Asyncio&lt;/code&gt;, on the other hand, uses &lt;a href=&quot;https://en.wikipedia.org/wiki/Cooperative_multitasking&quot;&gt;cooperative multitasking&lt;/a&gt;. The tasks must cooperate by announcing when they are ready to be switched out. That means that the code in the task has to change slightly to make this happen.&lt;/p&gt;
&lt;p&gt;The benefit of doing this extra work up front is that you always know where your task will be swapped out. It will not be swapped out in the middle of a Python statement unless that statement is marked. You&amp;rsquo;ll see later how this can simplify parts of your design.&lt;/p&gt;
&lt;h2 id=&quot;what-is-parallelism&quot;&gt;What Is Parallelism?&lt;/h2&gt;
&lt;p&gt;So far, you&amp;rsquo;ve looked at concurrency that happens on a single processor. What about all of those CPU cores your cool, new laptop has? How can you make use of them? &lt;code&gt;multiprocessing&lt;/code&gt; is the answer.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;multiprocessing&lt;/code&gt;, Python creates new processes. A process here can be thought of as almost a completely different program, though technically they&amp;rsquo;re usually defined as a collection of resources where the resources include memory, file handles and things like that. One way to think about it is that each process runs in its own Python interpreter.&lt;/p&gt;
&lt;p&gt;Because they are different processes, each of your trains of thought in a multiprocessing program can run on a different core. Running on a different core means that they actually can run at the same time, which is fabulous. There are some complications that arise from doing this, but Python does a pretty good job of smoothing them over most of the time.&lt;/p&gt;
&lt;p&gt;Now that you have an idea of what concurrency and parallelism are, let&amp;rsquo;s review their differences, and then we can look at why they can be useful:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concurrency Type&lt;/th&gt;
&lt;th&gt;Switching Decision&lt;/th&gt;
&lt;th&gt;Number of Processors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pre-emptive multitasking (&lt;code&gt;threading&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The operating system decides when to switch tasks external to Python.&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cooperative multitasking (&lt;code&gt;asyncio&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The tasks decide when to give up control.&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiprocessing (&lt;code&gt;multiprocessing&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The processes all run at the same time on different processors.&lt;/td&gt;
&lt;td&gt;Many&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Each of these types of concurrency can be useful. Let&amp;rsquo;s take a look at what types of programs they can help you speed up.&lt;/p&gt;
&lt;h2 id=&quot;when-is-concurrency-useful&quot;&gt;When Is Concurrency Useful?&lt;/h2&gt;
&lt;p&gt;Concurrency can make a big difference for two types of problems. These are generally called CPU-bound and I/O-bound.&lt;/p&gt;
&lt;p&gt;I/O-bound problems cause your program to slow down because it frequently must wait for input/output (I/O) from some external resource. They arise frequently when your program is working with things that are much slower than your CPU.&lt;/p&gt;
&lt;p&gt;Examples of things that are slower than your CPU are legion, but your program thankfully does not interact with most of them. The slow things your program will interact with most frequently are the file system and network connections.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see what that looks like:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/IOBound.4810a888b457.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-100&quot; src=&quot;https://files.realpython.com/media/IOBound.4810a888b457.png&quot; width=&quot;1737&quot; height=&quot;537&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/IOBound.4810a888b457.png&amp;amp;w=434&amp;amp;sig=9a0794c8f7e8424f5caf6d1bcbd555fcdd612b68 434w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/IOBound.4810a888b457.png&amp;amp;w=868&amp;amp;sig=f1a9d0fbb4e7f743a55ee0fec76eb627cfbf3571 868w, https://files.realpython.com/media/IOBound.4810a888b457.png 1737w&quot; sizes=&quot;75vw&quot; alt=&quot;Timing Diagram of an I/O Bound Program&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the diagram above, the blue boxes show time when your program is doing work, and the red boxes are time spent waiting for an I/O operation to complete. This diagram is not to scale because requests on the internet can take several orders of magnitude longer than CPU instructions, so your program can end up spending most of its time waiting. This is what your browser is doing most of the time.&lt;/p&gt;
&lt;p&gt;On the flip side, there are classes of programs that do significant computation without talking to the network or accessing a file. These are the CPU-bound programs, because the resource limiting the speed of your program is the CPU, not the network or the file system.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a corresponding diagram for a CPU-bound program:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/CPUBound.d2d32cb2626c.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-100&quot; src=&quot;https://files.realpython.com/media/CPUBound.d2d32cb2626c.png&quot; width=&quot;1737&quot; height=&quot;486&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CPUBound.d2d32cb2626c.png&amp;amp;w=434&amp;amp;sig=6bbdd46096cca225291e357d86691e08939744a4 434w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/CPUBound.d2d32cb2626c.png&amp;amp;w=868&amp;amp;sig=88b3c863f684b8109ed17e4a013aab1e666dba1f 868w, https://files.realpython.com/media/CPUBound.d2d32cb2626c.png 1737w&quot; sizes=&quot;75vw&quot; alt=&quot;Timing Diagram of an CPU Bound Program&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you work through the examples in the following section, you&amp;rsquo;ll see that different forms of concurrency work better or worse with CPU-bound and I/O-bound programs. Adding concurrency to your program adds extra code and complications, so you&amp;rsquo;ll need to decide if the potential speed up is worth the extra effort. By the end of this article, you should have enough info to start making that decision.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a quick summary to clarify this concept:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;I/O-Bound Process&lt;/th&gt;
&lt;th&gt;CPU-Bound Process&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Your program spends most of its time talking to a slow device, like a network connection, a hard drive, or a printer.&lt;/td&gt;
&lt;td&gt;You program spends most of its time doing CPU operations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speeding it up involves overlapping the times spent waiting for these devices.&lt;/td&gt;
&lt;td&gt;Speeding it up involves finding ways to do more computations in the same amount of time.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;You&amp;rsquo;ll look at I/O-bound programs first. Then, you&amp;rsquo;ll get to see some code dealing with CPU-bound programs.&lt;/p&gt;
&lt;h2 id=&quot;how-to-speed-up-an-io-bound-program&quot;&gt;How to Speed Up an I/O-Bound Program&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start by focusing on I/O-bound programs and a common problem: downloading content over the network. For our example, you will be downloading web pages from a few sites, but it really could be any network traffic. It&amp;rsquo;s just easier to visualize and set up with web pages.&lt;/p&gt;
&lt;h3 id=&quot;synchronous-version&quot;&gt;Synchronous Version&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;ll start with a non-concurrent version of this task. Note that this program requires the &lt;a href=&quot;http://docs.python-requests.org/en/master/&quot;&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; module. You should run &lt;code&gt;pip install requests&lt;/code&gt; before running it, probably using a &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;virtualenv&lt;/a&gt;. This version does not use concurrency at all:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Read {len(response.content)} from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{url}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://www.jython.org&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://olympus.realpython.org/dice&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Downloaded {len(sites)} in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, this is a fairly short program. &lt;code&gt;download_site()&lt;/code&gt; just downloads the contents from a URL and prints the size. One small thing to point out is that we&amp;rsquo;re using a &lt;a href=&quot;http://docs.python-requests.org/en/master/user/advanced/#session-objects&quot;&gt;&lt;code&gt;Session&lt;/code&gt;&lt;/a&gt; object from &lt;code&gt;requests&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is possible to simply use &lt;code&gt;get()&lt;/code&gt; from &lt;code&gt;requests&lt;/code&gt; directly, but creating a &lt;code&gt;Session&lt;/code&gt; object allows &lt;code&gt;requests&lt;/code&gt; to do some fancy networking tricks and really speed things up.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;download_all_sites()&lt;/code&gt; creates the &lt;code&gt;Session&lt;/code&gt; and then walks through the list of sites, downloading each one in turn. Finally, it prints out how long this process took so you can have the satisfaction of seeing how much concurrency has helped us in the following examples.&lt;/p&gt;
&lt;p&gt;The processing diagram for this program will look much like the I/O-bound diagram in the last section.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Network traffic is dependent on many factors that can vary from second to second. I&amp;rsquo;ve seen the times of these tests double from one run to another due to network issues.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Why the Synchronous Version Rocks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The great thing about this version of code is that, well, it&amp;rsquo;s easy. It was comparatively easy to write and debug. It&amp;rsquo;s also more straight-forward to think about. There&amp;rsquo;s only one train of thought running through it, so you can predict what the next step is and how it will behave.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Problems With the Synchronous Version&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The big problem here is that it&amp;rsquo;s relatively slow compared to the other solutions we&amp;rsquo;ll provide. Here&amp;rsquo;s an example of what the final output gave on my machine:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./io_non_concurrent.py
&lt;span class=&quot;go&quot;&gt;   [most output skipped]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Downloaded 160 in 14.289619207382202 seconds&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Your results may vary significantly. When running this script, I saw the times vary from 14.2 to 21.9 seconds. For this article, I took the fastest of three runs as the time. The differences between the methods will still be clear.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Being slower isn&amp;rsquo;t always a big issue, however. If the program you&amp;rsquo;re running takes only 2 seconds with a synchronous version and is only run rarely, it&amp;rsquo;s probably not worth adding concurrency. You can stop here.&lt;/p&gt;
&lt;p&gt;What if your program is run frequently? What if it takes hours to run? Let&amp;rsquo;s move on to concurrency by rewriting this program using &lt;code&gt;threading&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;threading-version&quot;&gt;&lt;code&gt;threading&lt;/code&gt; Version&lt;/h3&gt;
&lt;p&gt;As you probably guessed, writing a threaded program takes more effort. You might be surprised at how little extra effort it takes for simple cases, however. Here&amp;rsquo;s what the same program looks like with &lt;code&gt;threading&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;concurrent.futures&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;thread_local&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_local&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;session&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;thread_local&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread_local&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Read {len(response.content)} from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{url}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://www.jython.org&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://olympus.realpython.org/dice&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Downloaded {len(sites)} in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threadLocal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;session&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;threadLocal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threadLocal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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


&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;increment_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fake_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fake_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;increment_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fake_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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


&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Read &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{0}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{1}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content_length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aiohttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ensure_future&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;return_exceptions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://www.jython.org&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://olympus.realpython.org/dice&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_event_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run_until_complete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Downloaded {len(sites)} sites in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_global_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multiprocessing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:Read {len(response.content)} from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{url}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multiprocessing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_global_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://www.jython.org&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://olympus.realpython.org/dice&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;download_all_sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Downloaded {len(sites)} in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;That&amp;rsquo;s not surprising, as I/O-bound problems are not really why &lt;code&gt;multiprocessing&lt;/code&gt; exists. You&amp;rsquo;ll see more as you step into the next section and look at CPU-bound examples.&lt;/p&gt;
&lt;h2 id=&quot;how-to-speed-up-a-cpu-bound-program&quot;&gt;How to Speed Up a CPU-Bound Program&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s shift gears here a little bit. The examples so far have all dealt with an I/O-bound problem. Now, you&amp;rsquo;ll look into a CPU-bound problem. As you saw, an I/O-bound problem spends most of its time waiting for external operations, like a network call, to complete. A CPU-bound problem, on the other hand, does few I/O operations, and its overall execution time is a factor of how fast it can process the required data.&lt;/p&gt;
&lt;p&gt;For the purposes of our example, we&amp;rsquo;ll use a somewhat silly function to create something that takes a long time to run on the CPU. This function computes the sum of the squares of each number from 0 to the passed-in value:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cpu_bound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ll be passing in large numbers, so this will take a while. Remember, this is just a placeholder for your code that actually does something useful and requires significant processing time, like computing the roots of equations or sorting a large data structure.&lt;/p&gt;
&lt;h3 id=&quot;cpu-bound-synchronous-version&quot;&gt;CPU-Bound Synchronous Version&lt;/h3&gt;
&lt;p&gt;Now let&amp;rsquo;s look at the non-concurrent version of the example:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cpu_bound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_sums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cpu_bound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5_000_000&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;find_sums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Duration &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cpu_bound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_sums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multiprocessing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpu_bound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5_000_000&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;find_sums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Duration &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{duration}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;That&amp;rsquo;s much better than we saw with the other options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Problems With the &lt;code&gt;multiprocessing&lt;/code&gt; Version&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are some drawbacks to using &lt;code&gt;multiprocessing&lt;/code&gt;. They don&amp;rsquo;t really show up in this simple example, but splitting your problem up so each processor can work independently can sometimes be difficult.&lt;/p&gt;
&lt;p&gt;Also, many solutions require more communication between the processes. This can add some complexity to your solution that a non-concurrent program would not need to deal with.&lt;/p&gt;
&lt;h2 id=&quot;when-to-use-concurrency&quot;&gt;When to Use Concurrency&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve covered a lot of ground here, so let&amp;rsquo;s review some of the key ideas and then discuss some decision points that will help you determine which, if any, concurrency module you want to use in your project.&lt;/p&gt;
&lt;p&gt;The first step of this process is deciding if you &lt;em&gt;should&lt;/em&gt; use a concurrency module. While the examples here make each of the libraries look pretty simple, concurrency always comes with extra complexity and can often result in bugs that are difficult to find.&lt;/p&gt;
&lt;p&gt;Hold out on adding concurrency until you have a known performance issue and &lt;em&gt;then&lt;/em&gt; determine which type of concurrency you need. As &lt;a href=&quot;https://en.wikipedia.org/wiki/Donald_Knuth&quot;&gt;Donald Knuth&lt;/a&gt; has said, &amp;ldquo;Premature optimization is the root of all evil (or at least most of it) in programming.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve decided that you should optimize your program, figuring out if your program is CPU-bound or I/O-bound is a great next step. Remember that I/O-bound programs are those that spend most of their time waiting for something to happen while CPU-bound programs spend their time processing data or crunching numbers as fast as they can.&lt;/p&gt;
&lt;p&gt;As you saw, CPU-bound problems only really gain from using &lt;code&gt;multiprocessing&lt;/code&gt;. &lt;code&gt;threading&lt;/code&gt; and &lt;code&gt;asyncio&lt;/code&gt; did not help this type of problem at all.&lt;/p&gt;
&lt;p&gt;For I/O-bound problems, there&amp;rsquo;s a general rule of thumb in the Python community: &amp;ldquo;Use &lt;code&gt;asyncio&lt;/code&gt; when you can, &lt;code&gt;threading&lt;/code&gt; when you must.&amp;rdquo; &lt;code&gt;asyncio&lt;/code&gt; can provide the best speed up for this type of program, but sometimes you will require critical libraries that have not been ported to take advantage of &lt;code&gt;asyncio&lt;/code&gt;. Remember that any task that doesn&amp;rsquo;t give up control to the event loop will block all of the other tasks.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve now seen the basic types of concurrency available in Python:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;threading&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asyncio&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;multiprocessing&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&amp;rsquo;ve got the understanding to decide which concurrency method you should use for a given problem, or if you should use any at all! In addition, you&amp;rsquo;ve achieved a better understanding of some of the problems that can arise when you&amp;rsquo;re using concurrency.&lt;/p&gt;
&lt;p&gt;I hope you&amp;rsquo;ve learned a lot from this article and that you find a great use for concurrency in your own projects! Be sure to take our &amp;ldquo;Python Concurrency&amp;rdquo; quiz linked below to check your learning:&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;i class=&quot;fa fa-graduation-cap&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt; Take the Quiz:&lt;/strong&gt; Test your knowledge with our interactive “Python Concurrency” quiz. Upon completion you will receive a score so you can track your learning progress over time:&lt;/p&gt;&lt;p class=&quot;text-center my-2&quot;&gt;&lt;a class=&quot;btn btn-primary&quot; href=&quot;/quizzes/python-concurrency/&quot; target=&quot;_blank&quot;&gt;Take the Quiz »&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Django Migrations: A Primer</title>
      <id>https://realpython.com/django-migrations-a-primer/</id>
      <link href="https://realpython.com/django-migrations-a-primer/"/>
      <updated>2019-01-09T12:10:21+00:00</updated>
      <summary>In this tutorial, you’ll get comfortable with Django migrations and learn how to create database tables without writing any SQL, how to automatically modify your database after you changed your models, and how to revert changes made to your database.</summary>
      <content type="html">
        &lt;p&gt;Since version 1.7, Django has come with built-in support for database migrations. In Django, database migrations usually go hand in hand with models: whenever you code up a new model, you also generate a migration to create the necessary table in the database. However, migrations can do much more.&lt;/p&gt;
&lt;p&gt;You are going to learn how Django Migrations work and how you can get the most out of them over the course of four articles and one video:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1: Django Migrations: A Primer (current article)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href=&quot;https://realpython.com/digging-deeper-into-migrations/&quot;&gt;Digging Deeper into Migrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href=&quot;https://realpython.com/data-migrations/&quot;&gt;Data Migrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Video: &lt;a href=&quot;https://realpython.com/django-migrations-a-primer/#video&quot;&gt;Django 1.7 Migrations - primer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article, you&amp;rsquo;ll get comfortable with Django migrations and learn the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to create database tables without writing any SQL&lt;/li&gt;
&lt;li&gt;How to automatically modify your database after you changed your models&lt;/li&gt;
&lt;li&gt;How to revert changes made to your database&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;#&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-django-resources-learing-guide&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a free Django Learning Resources Guide (PDF)&lt;/a&gt; that shows you tips and tricks as well as common pitfalls to avoid when building Python + Django web applications.&lt;/p&gt;&lt;/div&gt;

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

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

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

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

&lt;p&gt;This is the basic model to keep track of Bitcoin prices.&lt;/p&gt;
&lt;p&gt;Also, don&amp;rsquo;t forget to add the newly created app to &lt;code&gt;settings.INSTALLED_APPS&lt;/code&gt;. Open &lt;code&gt;bitcoin_tracker/settings.py&lt;/code&gt; and append &lt;code&gt;historical_data&lt;/code&gt; to the list &lt;code&gt;INSTALLED_APPS&lt;/code&gt;, like this:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.admin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.sessions&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.messages&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.staticfiles&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;historical_data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The other settings are fine for this project. This tutorial assumes that your project is configured to use an SQLite database, which is the default.&lt;/p&gt;
&lt;h2 id=&quot;creating-migrations&quot;&gt;Creating Migrations&lt;/h2&gt;
&lt;p&gt;With the model created, the first thing you need to do is create a migration for it. You can do this with the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py makemigrations historical_data
&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;historical_data&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  historical_data/migrations/0001_initial.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - Create model PriceHistory&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Specifying the name of the application, &lt;code&gt;historical_data&lt;/code&gt;, is optional. Leaving it off creates migrations for all apps.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This creates the migrations file that instructs Django on how to create the database tables for the models defined in your application. Let&amp;rsquo;s have another look at the directory tree:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;bitcoin_tracker/
|
├── bitcoin_tracker/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
|
├── historical_data/
│   ├── migrations/
&lt;span class=&quot;hll&quot;&gt;│   │   ├── 0001_initial.py
&lt;/span&gt;│   │   └── __init__.py
|   |
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
|
├── db.sqlite3
└── manage.py
&lt;/pre&gt;&lt;/div&gt;

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

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

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

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

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

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

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

&lt;p&gt;Without migrations, you would have to figure out the SQL syntax to turn a &lt;code&gt;PositiveIntegerField&lt;/code&gt; into a &lt;code&gt;DecimalField&lt;/code&gt;. Luckily, Django will handle that for you. Just tell it to make migrations:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py makemigrations
&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;historical_data&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  historical_data/migrations/0002_auto_20181112_1950.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - Alter field volume on pricehistory&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The name of the migration file (&lt;code&gt;0002_auto_20181112_1950.py&lt;/code&gt;) is based on the current time and will be different if you follow along on your system.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now you apply this migration to your database:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: admin, auth, contenttypes, historical_data, sessions&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying historical_data.0002_auto_20181112_1950... OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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

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

&lt;p&gt;Now you can recreate it with a more descriptive name:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./manage.py makemigrations historical_data --name switch_to_decimals
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;Cheers!&lt;/p&gt;
&lt;h2 id=&quot;video&quot;&gt;Video&lt;/h2&gt;
&lt;div class=&quot;embed-responsive embed-responsive-16by9 mb-3&quot;&gt;
  &lt;iframe class=&quot;embed-responsive-item&quot; type=&quot;text/html&quot; src=&quot;https://www.youtube.com/embed/7PiyO-N6Pho?autoplay=0&amp;modestbranding=1&amp;rel=0&amp;showinfo=0&amp;origin=https://realpython.com&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>The Ultimate Guide to Python Type Checking</title>
      <id>https://realpython.com/python-type-checking/</id>
      <link href="https://realpython.com/python-type-checking/"/>
      <updated>2019-01-07T14:00:00+00:00</updated>
      <summary>In this guide, you&#39;ll look at Python type checking. Traditionally, types have been handled by the Python interpreter in a flexible but implicit way. Recent versions of Python allow you to specify explicit type hints that can be used by different tools to help you develop your code more efficiently.</summary>
      <content type="html">
        &lt;p&gt;In this guide, you will get a look into Python type checking. Traditionally, types have been handled by the Python interpreter in a flexible but implicit way. Recent versions of Python allow you to specify explicit type hints that can be used by different tools to help you develop your code more efficiently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial, you&amp;rsquo;ll learn about the following:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Type annotations and type hints&lt;/li&gt;
&lt;li&gt;Adding static types to code, both your code and the code of others&lt;/li&gt;
&lt;li&gt;Running a static type checker&lt;/li&gt;
&lt;li&gt;Enforcing types at runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a comprehensive guide that will cover a lot of ground. If you want to just get a quick glimpse of how type hints work in Python, and see whether type checking is something you would include in your code, you don&amp;rsquo;t need to read all of it. The two sections &lt;a href=&quot;#hello-types&quot;&gt;Hello Types&lt;/a&gt; and &lt;a href=&quot;#pros-and-cons&quot;&gt;Pros and Cons&lt;/a&gt; will give you a taste of how type checking works and recommendations about when it&amp;rsquo;ll be useful.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;type-systems&quot;&gt;Type Systems&lt;/h2&gt;
&lt;p&gt;All programming languages include some kind of &lt;a href=&quot;https://en.wikipedia.org/wiki/Type_system&quot;&gt;type system&lt;/a&gt; that formalizes which categories of objects it can work with and how those categories are treated. For instance, a type system can define a numerical type, with &lt;code&gt;42&lt;/code&gt; as one example of an object of numerical type.&lt;/p&gt;
&lt;h3 id=&quot;dynamic-typing&quot;&gt;Dynamic Typing&lt;/h3&gt;
&lt;p&gt;Python is a dynamically typed language. This means that the Python interpreter does type checking only as code runs, and that the type of a variable is allowed to change over its lifetime. The following dummy examples demonstrate that Python has dynamic typing:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;two&amp;quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This line never runs, so no TypeError is raised&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&lt;/span&gt;

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

&lt;p&gt;In the first example, the branch &lt;code&gt;1 + &quot;two&quot;&lt;/code&gt; never runs so it&amp;rsquo;s never type checked. The second example shows that when &lt;code&gt;1 + &quot;two&quot;&lt;/code&gt; is evaluated it raises a &lt;code&gt;TypeError&lt;/code&gt; since you can&amp;rsquo;t add an integer and a string in Python.&lt;/p&gt;
&lt;p&gt;Next, let&amp;rsquo;s see if variables can change type:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;class &amp;#39;str&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;28.1&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;class &amp;#39;float&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;p&gt;Note that the call to &lt;code&gt;len()&lt;/code&gt; gives the return value of the &lt;code&gt;.__len__()&lt;/code&gt; method. In fact, the implementation of &lt;code&gt;len()&lt;/code&gt; is essentially equivalent to the following:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__len__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;By default the function returns the headline left aligned with an underline. By setting the &lt;code&gt;align&lt;/code&gt; flag to &lt;code&gt;False&lt;/code&gt; you can alternatively have the headline be centered with a surrounding line of &lt;code&gt;o&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Python Type Checking&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--------------------&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;oooooooooooooo Python Type Checking oooooooooooooo&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It&amp;rsquo;s time for our first type hints! To add information about types to the function, you simply annotate its arguments and return value as follows:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;text: str&lt;/code&gt; syntax says that the &lt;code&gt;text&lt;/code&gt; argument should be of type &lt;code&gt;str&lt;/code&gt;. Similarly, the optional &lt;code&gt;align&lt;/code&gt; argument should have type &lt;code&gt;bool&lt;/code&gt; with the default value &lt;code&gt;True&lt;/code&gt;. Finally, the &lt;code&gt;-&amp;gt; str&lt;/code&gt; notation specifies that &lt;code&gt;headline()&lt;/code&gt; will return a string.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://realpython.com/python-pep8/&quot;&gt;terms of style&lt;/a&gt;, &lt;a href=&quot;https://www.python.org/dev/peps/pep-0008/#other-recommendations&quot;&gt;PEP 8&lt;/a&gt; recommends the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use normal rules for colons, that is, no space before and one space after a colon: &lt;code&gt;text: str&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use spaces around the &lt;code&gt;=&lt;/code&gt; sign when combining an argument annotation with a default value: &lt;code&gt;align: bool = True&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use spaces around the &lt;code&gt;-&amp;gt;&lt;/code&gt; arrow: &lt;code&gt;def headline(...) -&amp;gt; str&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adding type hints like this has no runtime effect: they are only hints and are not enforced on their own. For instance, if we use a wrong type for the (admittedly badly named) &lt;code&gt;align&lt;/code&gt; argument, the code still runs without any problems or warnings:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;left&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Python Type Checking&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--------------------&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The reason this seemingly works is that the string &lt;code&gt;&quot;left&quot;&lt;/code&gt; &lt;a href=&quot;https://realpython.com/python-operators-expressions/#evaluation-of-non-boolean-values-in-boolean-context&quot;&gt;compares as truthy&lt;/a&gt;. Using &lt;code&gt;align=&quot;center&quot;&lt;/code&gt; would not have the desired effect as &lt;code&gt;&quot;center&quot;&lt;/code&gt; is also truthy.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To catch this kind of error you can use a static type checker. That is, a tool that checks the types of your code without actually running it in the traditional sense.&lt;/p&gt;
&lt;p&gt;You might already have such a type checker built into your editor. For instance &lt;a href=&quot;https://www.jetbrains.com/pycharm/&quot;&gt;PyCharm&lt;/a&gt; immediately gives you a warning:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border w-66&quot; src=&quot;https://files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png&quot; width=&quot;748&quot; height=&quot;297&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png&amp;amp;w=187&amp;amp;sig=a258fbc862d03ec121a257be645b15168d1fb727 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png&amp;amp;w=374&amp;amp;sig=9510f9ce9f753dc9f55c8f9244dde031b4fb660f 374w, https://files.realpython.com/media/pycharm_type_error.76a49b9d4ff1.png 748w&quot; sizes=&quot;75vw&quot; alt=&quot;PyCharm flagging a type error&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The most common tool for doing type checking is &lt;a href=&quot;http://mypy-lang.org/&quot;&gt;Mypy&lt;/a&gt; though. You&amp;rsquo;ll get a short introduction to Mypy in a moment, while you can learn much more about how it works &lt;a href=&quot;#the-mypy-project&quot;&gt;later&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t already have Mypy on your system, you can install it using &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install mypy
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Put the following code in a file called &lt;code&gt;headlines.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# headlines.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;{text.title()}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&amp;#39;-&amp;#39; * len(text)}&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; {text.title()} &amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;o&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;use mypy&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;center&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;Based on the type hints, Mypy is able to tell us that we are using the wrong type on line 10.&lt;/p&gt;
&lt;p&gt;To fix the issue in the code you should change the value of the &lt;code&gt;align&lt;/code&gt; argument you are passing in. You might also rename the &lt;code&gt;align&lt;/code&gt; flag to something less confusing:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# headlines.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;centered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;centered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;{text.title()}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&amp;#39;-&amp;#39; * len(text)}&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; {text.title()} &amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;o&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;python type checking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;use mypy&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;centered&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you&amp;rsquo;ve changed &lt;code&gt;align&lt;/code&gt; to &lt;code&gt;centered&lt;/code&gt;, and correctly used a boolean value for &lt;code&gt;centered&lt;/code&gt; when calling &lt;code&gt;headline()&lt;/code&gt;. The code now passes Mypy:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy headlines.py
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No output from Mypy means that no type errors were detected. Furthermore, when you run the code you see the expected output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python headlines.py
&lt;span class=&quot;go&quot;&gt;Python Type Checking&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;oooooooooooooooooooo Use Mypy oooooooooooooooooooo&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;p&gt;On Linux it&amp;rsquo;s quite easy to check how much time the &lt;code&gt;typing&lt;/code&gt; import takes using the &lt;a href=&quot;https://perf.wiki.kernel.org/&quot;&gt;&lt;code&gt;perf&lt;/code&gt; utility&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; perf stat -r &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt; python3.6 import_typing.py

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

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

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

&lt;p&gt;So running the &lt;code&gt;import typing.py&lt;/code&gt; script takes about 45 milliseconds. Of course this is not all time spent on importing &lt;code&gt;typing&lt;/code&gt;. Some of this is overhead in starting the Python interpreter, so let&amp;rsquo;s compare to running Python on an empty file:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; perf stat -r &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt; python3.6 empty_file.py

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

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

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

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

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

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

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

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

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

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

&lt;p&gt;For arguments the syntax is &lt;code&gt;argument: annotation&lt;/code&gt;, while the return type is annotated using &lt;code&gt;-&amp;gt; annotation&lt;/code&gt;. Note that the annotation must be a valid Python expression.&lt;/p&gt;
&lt;p&gt;The following simple example adds annotations to a function that calculates the circumference of a circle:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;math&lt;/span&gt;

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

&lt;p&gt;When running the code, you can also inspect the annotations. They are stored in a special &lt;code&gt;.__annotations__&lt;/code&gt; attribute on the function:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circumference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;7.728317927830891&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circumference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__annotations__&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;radius&amp;#39;: &amp;lt;class &amp;#39;float&amp;#39;&amp;gt;, &amp;#39;return&amp;#39;: &amp;lt;class &amp;#39;float&amp;#39;&amp;gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;Next, run this code through Mypy:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy reveal.py
&lt;span class=&quot;go&quot;&gt;reveal.py:4: error: Revealed type is &amp;#39;builtins.float&amp;#39;&lt;/span&gt;

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

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

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

&lt;p&gt;The variable &lt;code&gt;pi&lt;/code&gt; has been annotated with the &lt;code&gt;float&lt;/code&gt; type hint.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Static type checkers are more than able to figure out that &lt;code&gt;3.142&lt;/code&gt; is a float, so in this example the annotation of &lt;code&gt;pi&lt;/code&gt; is not necessary. As you learn more about the Python type system, you&amp;rsquo;ll see more relevant examples of variable annotations.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Annotations of variables are stored in the module level &lt;code&gt;__annotations__&lt;/code&gt; dictionary:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circumference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;6.284&lt;/span&gt;

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

&lt;p&gt;You&amp;rsquo;re allowed to annotate a variable without giving it a value. This adds the annotation to the &lt;code&gt;__annotations__&lt;/code&gt; dictionary, while the variable remains undefined:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nothing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nothing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;NameError: name &amp;#39;nothing&amp;#39; is not defined&lt;/span&gt;

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

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

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

&lt;p&gt;The type comments are just comments, so they can be used in any version of Python.&lt;/p&gt;
&lt;p&gt;Type comments are handled directly by the type checker, so these types are not available in the &lt;code&gt;__annotations__&lt;/code&gt; dictionary:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circumference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__annotations__&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A type comment must start with the &lt;code&gt;type:&lt;/code&gt; literal, and be on the same or the following line as the function definition. If you want to annotate a function with several arguments, you write each type separated by comma:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill_char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;-&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# type: (str, int, str) -&amp;gt; str&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; {text.title()} &amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill_char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;type comments work&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You are also allowed to write each argument on a separate line with its own annotation:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# headlines.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# type: str&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# type: int&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;fill_char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;-&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# type: str&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;# type: (...) -&amp;gt; str&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; {text.title()} &amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill_char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;type comments work&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Run the example through Python and Mypy:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;  python headlines.py
&lt;span class=&quot;go&quot;&gt;---------- Type Comments Work ----------&lt;/span&gt;

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

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

&lt;p&gt;You can also add type comments to variables. This is done similarly to how you add type comments to arguments:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.142&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# type: float&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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

&lt;p&gt;With composite types, you are allowed to do the same:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tuple&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;centered&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;capitalize&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, this does not really tell the full story. What will be the types of &lt;code&gt;names[2]&lt;/code&gt;, &lt;code&gt;version[0]&lt;/code&gt;, and &lt;code&gt;options[&quot;centered&quot;]&lt;/code&gt;? In this concrete case you can see that they are &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, and &lt;code&gt;bool&lt;/code&gt;, respectively. However, the type hints themselves give no information about this.&lt;/p&gt;
&lt;p&gt;Instead, you should use the special types defined in the &lt;a href=&quot;https://docs.python.org/library/typing.html&quot;&gt;&lt;code&gt;typing&lt;/code&gt; module&lt;/a&gt;. These types add syntax for specifying the types of elements of composite types. You can write the following:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;centered&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;capitalize&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that each of these types start with a capital letter and that they all use square brackets to define item types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;names&lt;/code&gt;&lt;/strong&gt; is a list of strings&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;&lt;/strong&gt; is a 3-tuple consisting of three integers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;options&lt;/code&gt;&lt;/strong&gt; is a dictionary mapping strings to boolean values&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;typing&lt;/code&gt; module contains many more composite types, including &lt;code&gt;Counter&lt;/code&gt;, &lt;code&gt;Deque&lt;/code&gt;, &lt;code&gt;FrozenSet&lt;/code&gt;, &lt;code&gt;NamedTuple&lt;/code&gt;, and &lt;code&gt;Set&lt;/code&gt;. In addition, the module includes other kinds of types that you&amp;rsquo;ll see in later sections.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s return to the card game. A card is represented by a tuple of two strings. You can write this as &lt;code&gt;Tuple[str, str]&lt;/code&gt;,  so the type of the deck of cards becomes &lt;code&gt;List[Tuple[str, str]]&lt;/code&gt;. Therefore you can annotate &lt;code&gt;create_deck()&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Create a new deck of 52 cards&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SUITS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;Sequence&lt;/code&gt; is an example of using duck typing. A &lt;code&gt;Sequence&lt;/code&gt; is anything that supports &lt;code&gt;len()&lt;/code&gt; and &lt;code&gt;.__getitem__()&lt;/code&gt;, independent of its actual type.&lt;/p&gt;
&lt;h3 id=&quot;type-aliases&quot;&gt;Type Aliases&lt;/h3&gt;
&lt;p&gt;The type hints might become quite oblique when working with nested types like the deck of cards. You may need to stare at &lt;code&gt;List[Tuple[str, str]]&lt;/code&gt; a bit before figuring out that it matches our representation of a deck of cards.&lt;/p&gt;
&lt;p&gt;Now consider how you would annotate &lt;code&gt;deal_hands()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deal_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Deal the cards in the deck into four hands&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s just terrible!&lt;/p&gt;
&lt;p&gt;Recall that type annotations are regular Python expressions. That means that you can define your own type aliases by assigning them to new variables. You can for instance create &lt;code&gt;Card&lt;/code&gt; and &lt;code&gt;Deck&lt;/code&gt; type aliases:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;Card&lt;/code&gt; can now be used in type hints or in the definition of new type aliases, like &lt;code&gt;Deck&lt;/code&gt; in the example above.&lt;/p&gt;
&lt;p&gt;Using these aliases, the annotations of &lt;code&gt;deal_hands()&lt;/code&gt; become much more readable:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deal_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Deal the cards in the deck into four hands&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Type aliases are great for making your code and its intent clearer. At the same time, these aliases can be inspected to see what they represent:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;typing.List[typing.Tuple[str, str]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that when printing &lt;code&gt;Deck&lt;/code&gt;, it shows that it&amp;rsquo;s an alias for a list of 2-tuples of strings.&lt;/p&gt;
&lt;h3 id=&quot;functions-without-return-values&quot;&gt;Functions Without Return Values&lt;/h3&gt;
&lt;p&gt;You may know that functions without an explicit return still return &lt;code&gt;None&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{player_name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; plays&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret_val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Jacob&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Jacob plays&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While such functions technically return something, that return value is not useful. You should add type hints saying as much by using &lt;code&gt;None&lt;/code&gt; also as the return type:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# play.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{player_name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; plays&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret_val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Filip&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The annotations help catch the kinds of subtle bugs where you are trying to use a meaningless return value. Mypy will give you a helpful warning:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy play.py
&lt;span class=&quot;go&quot;&gt;play.py:6: error: &amp;quot;play&amp;quot; does not return a value&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that being explicit about a function not returning anything is different from not adding a type hint about the return value:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# play.py&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{player_name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; plays&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

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

&lt;p&gt;In this latter case Mypy has no information about the return value so it will not generate any warning:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy play.py
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a more exotic case, note that you can also annotate functions that are never expected to return normally. This is done using &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/more_types.html#the-noreturn-type&quot;&gt;&lt;code&gt;NoReturn&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NoReturn&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;black_hole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NoReturn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;There is no going back ...&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means more or less what it says: &lt;code&gt;items&lt;/code&gt; is a sequence that can contain items of any type and &lt;code&gt;choose()&lt;/code&gt; will return one such item of any type. Unfortunately, this is not that useful. Consider the following example:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# choose.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While Mypy will correctly infer that &lt;code&gt;names&lt;/code&gt; is a list of strings, that information is lost after the call to &lt;code&gt;choose()&lt;/code&gt; because of the use of the &lt;code&gt;Any&lt;/code&gt; type:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy choose.py
&lt;span class=&quot;go&quot;&gt;choose.py:10: error: Revealed type is &amp;#39;builtins.list[builtins.str*]&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose.py:13: error: Revealed type is &amp;#39;Any&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;issubclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since 0 and 1 are both integers, the first condition holds. Above you can see that booleans can be added together, but they can also do anything else integers can. This is the second condition above. In other words, &lt;code&gt;bool&lt;/code&gt; is a subtype of &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The importance of subtypes is that a subtype can always pretend to be its supertype. For instance, the following code type checks as correct:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Passing in bool instead of int&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The problem with using &lt;code&gt;Any&lt;/code&gt; is that you are needlessly losing type information. You know that if you pass a list of strings to &lt;code&gt;choose()&lt;/code&gt;, it will return a string. Below you&amp;rsquo;ll see how to express this using type variables, as well as how to work with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#duck-types-and-protocols&quot;&gt;Duck types and protocols&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Arguments with &lt;a href=&quot;#the-optional-type&quot;&gt;&lt;code&gt;None&lt;/code&gt; as default value&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#type-hints-for-methods&quot;&gt;Class methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#classes-as-types&quot;&gt;The type of your own classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#annotating-args-and-kwargs&quot;&gt;Variable number of arguments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;type-variables&quot;&gt;Type Variables&lt;/h3&gt;
&lt;p&gt;A type variable is a special variable that can take on any type, depending on the situation.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s create a type variable that will effectively encapsulate the behavior of &lt;code&gt;choose()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# choose.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Choosable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Chooseable&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Choosable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Choosable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A type variable must be defined using &lt;code&gt;TypeVar&lt;/code&gt; from the &lt;code&gt;typing&lt;/code&gt; module. When used, a type variable ranges over all possible types and takes the most specific type possible. In the example, &lt;code&gt;name&lt;/code&gt; is now a &lt;code&gt;str&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy choose.py
&lt;span class=&quot;go&quot;&gt;choose.py:12: error: Revealed type is &amp;#39;builtins.list[builtins.str*]&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;choose.py:15: error: Revealed type is &amp;#39;builtins.str*&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Consider a few other examples:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# choose_examples.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;choose&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Guido&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Jukka&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ivan&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reveal_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Python&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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

&lt;p&gt;Also note that in the second example the type is considered &lt;code&gt;float&lt;/code&gt; even though the input list only contains &lt;code&gt;int&lt;/code&gt; objects. This is because &lt;code&gt;Choosable&lt;/code&gt; was restricted to strings and floats and &lt;code&gt;int&lt;/code&gt; is a subtype of &lt;code&gt;float&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In our card game we want to restrict &lt;code&gt;choose()&lt;/code&gt; to be used for &lt;code&gt;str&lt;/code&gt; and &lt;code&gt;Card&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Choosable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Choosable&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Choosable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Choosable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We briefly mentioned that &lt;code&gt;Sequence&lt;/code&gt; represents both lists and tuples. As we noted, a &lt;code&gt;Sequence&lt;/code&gt; can be thought of as a duck type, since it can be any object with &lt;code&gt;.__len__()&lt;/code&gt; and &lt;code&gt;.__getitem__()&lt;/code&gt; implemented.&lt;/p&gt;
&lt;h3 id=&quot;duck-types-and-protocols&quot;&gt;Duck Types and Protocols&lt;/h3&gt;
&lt;p&gt;Recall the following example from &lt;a href=&quot;#duck-typing&quot;&gt;the introduction&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__len__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;len()&lt;/code&gt; can return the length of any object that has implemented the &lt;code&gt;.__len__()&lt;/code&gt; method. How can we add type hints to &lt;code&gt;len()&lt;/code&gt;, and in particular the &lt;code&gt;obj&lt;/code&gt; argument?&lt;/p&gt;
&lt;p&gt;The answer hides behind the academic sounding term &lt;a href=&quot;https://en.wikipedia.org/wiki/Structural_type_system&quot;&gt;structural subtyping&lt;/a&gt;. One way to categorize type systems is by whether they are &lt;strong&gt;nominal&lt;/strong&gt; or &lt;strong&gt;structural&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In a &lt;strong&gt;nominal&lt;/strong&gt; system, comparisons between types are based on names and declarations. The Python type system is mostly nominal, where an &lt;code&gt;int&lt;/code&gt; can be used in place of a &lt;code&gt;float&lt;/code&gt; because of their subtype relationship.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In a &lt;strong&gt;structural&lt;/strong&gt; system, comparisons between types are based on structure. You could define a structural type &lt;code&gt;Sized&lt;/code&gt; that includes all instances that define &lt;code&gt;.__len__()&lt;/code&gt;, irrespective of their nominal type.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is ongoing work to bring a full-fledged structural type system to Python through &lt;a href=&quot;https://www.python.org/dev/peps/pep-0544/&quot;&gt;PEP 544&lt;/a&gt; which aims at adding a concept called protocols. Most of PEP 544 is already &lt;a href=&quot;https://mypy.readthedocs.io/en/latest/protocols.html&quot;&gt;implemented in Mypy&lt;/a&gt; though.&lt;/p&gt;
&lt;p&gt;A protocol specifies one or more methods that must be implemented. For example, all classes defining &lt;code&gt;.__len__()&lt;/code&gt; fulfill the &lt;code&gt;typing.Sized&lt;/code&gt; protocol. We can therefore annotate &lt;code&gt;len()&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sized&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__len__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__len__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__len__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;The challenge this creates for type hinting is that in general &lt;code&gt;start&lt;/code&gt; should be a string. However, it may also take the special non-string value &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In order to annotate such arguments you can use the &lt;code&gt;Optional&lt;/code&gt; type:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;Optional&lt;/code&gt; type simply says that a variable either has the type specified or is &lt;code&gt;None&lt;/code&gt;. An equivalent way of specifying the same would be using the &lt;code&gt;Union&lt;/code&gt; type: &lt;code&gt;Union[None, str]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Note that when using either &lt;code&gt;Optional&lt;/code&gt; or &lt;code&gt;Union&lt;/code&gt; you must take care that the variable has the correct type when you operate on it. This is done in the example by testing whether &lt;code&gt;start is None&lt;/code&gt;. Not doing so would cause both static type errors as well as possible runtime errors:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# player_order.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Mypy tells you that you have not taken care of the case where &lt;code&gt;start&lt;/code&gt; is &lt;code&gt;None&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy player_order.py
&lt;span class=&quot;go&quot;&gt;player_order.py:8: error: Argument 1 to &amp;quot;index&amp;quot; of &amp;quot;list&amp;quot; has incompatible&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                          type &amp;quot;Optional[str]&amp;quot;; expected &amp;quot;str&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The use of &lt;code&gt;None&lt;/code&gt; for optional arguments is so common that Mypy handles it automatically. Mypy assumes that a default argument of &lt;code&gt;None&lt;/code&gt; indicates an optional argument even if the type hint does not explicitly say so. You could have used the following:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;Now let&amp;rsquo;s add types to this code.&lt;/p&gt;
&lt;h3 id=&quot;type-hints-for-methods&quot;&gt;Type Hints for Methods&lt;/h3&gt;
&lt;p&gt;First of all type hints for methods work much the same as type hints for functions. The only difference is that the &lt;code&gt;self&lt;/code&gt; argument need not be annotated, as it always will be a class instance. The types of the &lt;code&gt;Card&lt;/code&gt; class are easy to add:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;SUITS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;♠ ♡ ♢ ♣&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2 3 4 5 6 7 8 9 10 J Q K A&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__repr__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.suit}{self.rank}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;p&gt;Note that the &lt;code&gt;Player&lt;/code&gt; class also will reference the &lt;code&gt;Deck&lt;/code&gt; class. This is however no problem, since &lt;code&gt;Deck&lt;/code&gt; is defined before &lt;code&gt;Player&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;34 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36 &lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;37 &lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;p&gt;While the code runs without problems, Mypy will flag a problem:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy dogs.py
&lt;span class=&quot;go&quot;&gt;dogs.py:24: error: &amp;quot;Animal&amp;quot; has no attribute &amp;quot;bark&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dogs.py:25: error: &amp;quot;Animal&amp;quot; has no attribute &amp;quot;bark&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The issue is that even though the inherited &lt;code&gt;Dog.newborn()&lt;/code&gt; and &lt;code&gt;Dog.twin()&lt;/code&gt; methods will return a &lt;code&gt;Dog&lt;/code&gt; the annotation says that they return an &lt;code&gt;Animal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In cases like this you want to be more careful to make sure the annotation is correct. The return type should match the type of &lt;code&gt;self&lt;/code&gt; or the instance type of &lt;code&gt;cls&lt;/code&gt;. This can be done using type variables that keep track of what is actually passed to &lt;code&gt;self&lt;/code&gt; and &lt;code&gt;cls&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# dogs.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;

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

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;birthday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;birthday&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;birthday&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newborn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TAnimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAnimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;twin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAnimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAnimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__class__&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;birthday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; says woof!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

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

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

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

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

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

&lt;p&gt;You can download this code and other examples from &lt;a href=&quot;https://github.com/realpython/materials/tree/master/python-type-checking&quot;&gt;GitHub&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# hearts.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;collections&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Union&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;overload&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SUITS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;♠ ♡ ♢ ♣&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2 3 4 5 6 7 8 9 10 J Q K A&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@property&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;The value of a card is rank as a number&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@property&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Points this card is worth&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;♠&amp;quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Q&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;♡&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__eq__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rank&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__lt__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__repr__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.suit}{self.rank}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Deck&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Create a new deck of 52 cards&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RANKS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SUITS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Play one card by removing it from the deck&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Deck&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Deal the cards in the deck into a number of hands&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_hands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Add a list of cards to the deck&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__len__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@overload&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__getitem__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@overload&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__getitem__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Deck&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__getitem__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Deck&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__class__&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Indices must be integers or slices&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__repr__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;repr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;playable_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;List which cards in hand are playable this round&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;♣&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;♣&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;♡&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;non_winning_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;List playable cards that are guaranteed to not win the trick&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([])&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;best_card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_card&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play_card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Play a card from a cpu player&amp;#39;s hand&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;playable_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;non_winning&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;non_winning_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# Strategy&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;non_winning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Highest card not winning the trick, prefer points&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;non_winning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Lowest card maybe winning, avoid points&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Highest card guaranteed winning, avoid points&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -&amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{card}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;has_card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__repr__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.__class__.__name__}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name!r}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.hand}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&amp;quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HumanPlayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play_card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Play a card from a human player&amp;#39;s hand&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;playable_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;  &amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{n}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{c}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;np_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;repr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{p_str}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  (Rest: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{np_str}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;card_num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, choose card: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ne&quot;&gt;ValueError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;IndexError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; =&amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{card}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HeartsGame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;P1 P2 P3 P4&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())[:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:]]&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HumanPlayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Play a game of Hearts until one player go bust&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()):&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Starting new round:&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;round_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play_round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;round_score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Scores:&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_score&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;most_common&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name:&amp;lt;15}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{round_score[name]:&amp;gt;3}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{total_score:&amp;gt;3}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;winners&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;names&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())]&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&amp;#39; and &amp;#39;.join(winners)} won the game&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play_round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Play a round of the Hearts card game&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;start_player&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has_card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;♣&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tricks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;hearts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# Play cards from each player&amp;#39;s hand until empty&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;turn_order&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turn_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;card&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play_card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts_broken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hearts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;start_player&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trick_winner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turn_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tricks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{start_player.name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; wins the trick&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;hearts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hearts&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;♡&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;played&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tricks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;player_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Rotate player order so that start goes first&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@staticmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trick_winner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;valid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lead&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@staticmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;count_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tricks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cards&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tricks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Read player names from the command line&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;player_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HeartsGame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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

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

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

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

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

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

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

&lt;p&gt;With Mypy installed, you can run it as a regular command line program:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy my_program.py
&lt;/pre&gt;&lt;/div&gt;

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

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

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

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

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

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

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

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

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

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

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

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

&lt;p&gt;Parse can be used to recognize simple patterns. Here is a small program that tries its best to figure out your name:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# parse_name.py&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;parse&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;patterns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;my name is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;i&amp;#39;m &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;i am &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;call me &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patterns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;What is your name? &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Hi &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, nice to meet you!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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

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

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

&lt;p&gt;If you have set everything up correctly, you should see this new error message. Mypy uses the new &lt;code&gt;parse.pyi&lt;/code&gt; file to figure out which functions are available in the &lt;code&gt;parse&lt;/code&gt; package. Since the stub file is empty, Mypy assumes that &lt;code&gt;parse.parse()&lt;/code&gt; does not exist, and then gives the error you see above.&lt;/p&gt;
&lt;p&gt;The following example does not add types for the whole &lt;code&gt;parse&lt;/code&gt; package. Instead it shows the type hints you need to add in order for Mypy to type check your use of &lt;code&gt;parse.parse()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# parse.pyi&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Union&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;spans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__getitem__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__repr__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;evaluate_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;case_sensitive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The ellipsis &lt;code&gt;...&lt;/code&gt; are part of the file, and should be written exactly as above. The stub file should only contain type hints for variables, attributes, functions, and methods, so the implementations should be left out and replaced by &lt;code&gt;...&lt;/code&gt; markers.&lt;/p&gt;
&lt;p&gt;Finally Mypy is able to spot the bug we introduced:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy parse_name.py
&lt;span class=&quot;go&quot;&gt;parse_name.py:16: error: Incompatible return value type (got&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                         &amp;quot;Result&amp;quot;, expected &amp;quot;str&amp;quot;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in grams&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;books&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To create a new book, you provide a name, price, and weight:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;naive.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Product: Python Tricks&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To create a cart, you first need to associate it with a user:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.auth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;haki&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;haki&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;naive.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;haki&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then the user can start adding items to it:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: Python Tricks&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;physical&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;virtual&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_CHOICES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Physical&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Virtual&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;choices&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_CHOICES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Common attributes&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Specific attributes&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in grams&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URLField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blank&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;[{self.get_type_display()}] &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;books&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, you added a type field to indicate what type of book it is. Then, you added a URL field to store the download link of the e-book.&lt;/p&gt;
&lt;p&gt;To add a physical book to your bookstore, do the following:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sparse.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physical_book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physical_book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: [Physical] Python Tricks&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To add a new e-book, you do the following:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtual_book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;The Old Man and the Sea&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://books.com/12345&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtual_book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: [Virtual] The Old Man and the Sea&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your users can now add both books and e-books to the cart:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sparse.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physical_book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;virtual_book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: [Physical] Python Tricks&amp;gt;, &amp;lt;Book: [Virtual] The Old Man and the Sea&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The virtual books are a big hit, and you decide to hire employees. The new employees are apparently not so tech savvy, and you start seeing weird things in the database:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://books.com/54321&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That book apparently weighs &lt;code&gt;0&lt;/code&gt; pounds and has a download link.&lt;/p&gt;
&lt;p&gt;This e-book apparently weighs 100g and has no download link:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This doesn&amp;rsquo;t make any sense. You have a data integrity problem.&lt;/p&gt;
&lt;p&gt;To overcome integrity problems, you add validations to the model:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.core.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;


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

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

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;A virtual product weight cannot exceed zero.&amp;#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;A virtual product must have a download link.&amp;#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;A physical product weight must exceed zero.&amp;#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;A physical product cannot have a download link.&amp;#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Unknown product type &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.type}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;quot;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;physical&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;virtual&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TYPE_CHOICES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Physical&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Virtual&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;choices&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_CHOICES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Common attributes&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JSONField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;[{self.get_type_display()}] &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;books&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;related_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;+&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;JSONField:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this example, you use PostgreSQL as a database backend. Django provides a built-in JSON field for PostgreSQL in &lt;code&gt;django.contrib.postgres.fields&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For other databases, such as SQLite and MySQL, there are &lt;a href=&quot;https://djangopackages.org/grids/g/json-fields/&quot;&gt;packages&lt;/a&gt; that provide similar functionality.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Your &lt;code&gt;Book&lt;/code&gt; model is now clutter-free. Common attributes are modeled as fields. Attributes that are not common to all types of products are stored in the &lt;code&gt;extra&lt;/code&gt; JSON field:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;semi_structured.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physical_book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;weight&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physical_book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_clean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physical_book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: [Physical] Python Tricks&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtual_book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;The Old Man and the Sea&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;download_link&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http://books.com/12345&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtual_book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_clean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtual_book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: [Virtual] The Old Man and the Sea&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;semi_structured.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physical_book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;virtual_book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: [Physical] Python Tricks&amp;gt;, &amp;lt;Book: [Virtual] The Old Man and the Sea&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Clearing up the clutter is important, but it comes with a cost. The validation logic is a lot more complicated:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.core.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.core.validators&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;URLValidator&lt;/span&gt;

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

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

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;weight&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ValueError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;Weight must be a number&amp;#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;KeyError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;s1&quot;&gt;&amp;#39;A virtual product weight cannot exceed zero.&amp;#39;&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;download_link&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;KeyError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;# Will raise a validation error&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;URLValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_PHYSICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;weight&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ValueError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&amp;#39;Weight must be a number&amp;#39;&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;KeyError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;s1&quot;&gt;&amp;#39;A physical product weight must exceed zero.&amp;#39;&lt;/span&gt;
                     &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;download_link&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;KeyError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;s1&quot;&gt;&amp;#39;A physical product cannot have a download link.&amp;#39;&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Unknown product type &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.type}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The benefit of using a proper field is that it validates the type. Both Django and the Django ORM can perform checks to make sure the right type is used for the field. When using a &lt;code&gt;JSONField&lt;/code&gt;, you need to validate both the type and the value:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE_VIRTUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;weight&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_clean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ValidationError: {&amp;#39;__all__&amp;#39;: [&amp;#39;A virtual product weight cannot exceed zero.&amp;#39;]}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another issue with using JSON is that not all databases have proper support for querying and indexing values in JSON fields.&lt;/p&gt;
&lt;p&gt;In PostgreSQL for example, you can query all the books that weigh more than &lt;code&gt;100&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra__weight__gt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: [Physical] Python Tricks&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in grams&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


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

&lt;p&gt;Notice that both &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;EBook&lt;/code&gt; inherit from &lt;code&gt;Product&lt;/code&gt;. The fields defined in the base class &lt;code&gt;Product&lt;/code&gt; are inherited, so the derived models &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;Ebook&lt;/code&gt; don&amp;rsquo;t need to repeat them.&lt;/p&gt;
&lt;p&gt;To add new products, you use the derived classes:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;abstract_base_model.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: Python Tricks&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EBook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;The Old Man and the Sea&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://books.com/12345&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: The Old Man and the Sea&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You might have noticed that the &lt;code&gt;Cart&lt;/code&gt; model is missing. You can try to create a &lt;code&gt;Cart&lt;/code&gt; model with a &lt;code&gt;ManyToMany&lt;/code&gt; field to &lt;code&gt;Product&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;primary_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

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

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

&lt;p&gt;You can now add both books and e-books to the cart:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebooks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This model is a bit more complicated now. Let’s query the total price of the items in the cart:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sum&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db.models.functions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Coalesce&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;Coalesce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;books__price&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;ebooks__price&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;total_price&amp;#39;: 1000}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in cents&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;


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


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

&lt;p&gt;The only difference between this example and the previous one is that the &lt;code&gt;Product&lt;/code&gt; model is not defined with &lt;code&gt;abstract=True&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To create new products, you use derived &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;EBook&lt;/code&gt; models directly:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;concrete_base_model.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EBook&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Python Tricks&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: Python Tricks&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EBook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;The Old Man and the Sea&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;download_link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://books.com/12345&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: The Old Man and the Sea&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the case of concrete base class, it’s interesting to see what&amp;rsquo;s happening in the underlying database. Let’s look at the tables created by Django in the database:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\d&lt;/span&gt; concrete_base_model_product

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

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

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

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

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

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

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

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

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

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

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

&lt;p&gt;Adding items to the cart is the same as before:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;concrete_base_model.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;Book: Python Tricks&amp;gt;, &amp;lt;Book: The Old Man and the Sea&amp;gt;]&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Working with common fields is also simple:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sum&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;price&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;total_price&amp;#39;: 2500}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Migrating base classes in Django:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When a derived model is created, Django adds a &lt;code&gt;bases&lt;/code&gt; attribute to the migration:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Book&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;      &lt;span class=&quot;n&quot;&gt;bases&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;concrete_base_model.product&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,),&lt;/span&gt;
&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneToOneField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_user_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;primary_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Unlike previous &lt;code&gt;Cart&lt;/code&gt; models, this &lt;code&gt;Cart&lt;/code&gt; no longer includes a &lt;code&gt;ManyToMany&lt;/code&gt; field.  You are going need to do that yourself.&lt;/p&gt;
&lt;p&gt;To represent a single item in the cart, you need to reference both the cart and any product:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.contenttypes.fields&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GenericForeignKey&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib.contenttypes.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;


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

&lt;p&gt;To add a new item in the Cart, you provide the content type and the primary key:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EBook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Adding an item to a cart is a common task. You can add a method on the cart to add any product to the cart:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

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

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;CartItem&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Adding a new item to a cart is now much shorter:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Getting information about the items in the cart is also possible:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;QuerySet [&amp;lt;CartItem: CartItem object (1)&amp;gt;, &amp;lt;CartItem: CartItem object (2)&amp;gt;]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Book: Python Tricks&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

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

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

&lt;p&gt;Using the reverse relation, you can answer questions like how many carts include a specific book:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart_items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;books__id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The two statement are identical.&lt;/p&gt;
&lt;p&gt;You still need to know the price of the entire cart. You already saw that fetching the price from each product table is impossible using the ORM. To do that, you have to iterate the items, fetch each item separately, and aggregate:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2500&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

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

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The use of class attributes and ellipses (&lt;code&gt;...&lt;/code&gt;) in the method definition are new features in Python 3.7. In earlier versions of Python, it isn&amp;rsquo;t possible to define a Protocol using this syntax. Instead of an ellipsis, methods should have &lt;code&gt;pass&lt;/code&gt; in the body. Class attributes such as &lt;code&gt;pk&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; can be defined using the &lt;code&gt;@attribute&lt;/code&gt; decorator, but it will not work with Django models.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can now use the &lt;code&gt;Product&lt;/code&gt; protocol to add type information. For example, in &lt;code&gt;add_item()&lt;/code&gt;, you accept an instance of a product and add it to the cart:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;CartItem&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Running &lt;code&gt;mypy&lt;/code&gt; on this function will not yield any warnings. Let&amp;rsquo;s say you change &lt;code&gt;product.pk&lt;/code&gt; to &lt;code&gt;product.id&lt;/code&gt;, which is not defined in the &lt;code&gt;Product&lt;/code&gt; protocol:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;CartItem&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_for_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CartItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product_content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;product_object_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will get the following warning from Mypy:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mypy
&lt;span class=&quot;go&quot;&gt;models.py:62: error: &amp;quot;Product&amp;quot; has no attribute &amp;quot;id&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;Protocol&lt;/code&gt; is not yet a part of Mypy. It&amp;rsquo;s part of the complementary package called &lt;a href=&quot;https://pypi.org/project/mypy_extensions/&quot;&gt;&lt;code&gt;mypy_extentions&lt;/code&gt;&lt;/a&gt;. The package is developed by the Mypy team and includes features that they thought weren&amp;rsquo;t ready for the main Mypy package yet.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Migrations are not needed to add product types:&lt;/strong&gt; The generic foreign key can reference any model. Adding a new type of product does not require migrations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Any model can be used as an item:&lt;/strong&gt; Using generic foreign key, any model can be referenced by the &lt;code&gt;Item&lt;/code&gt; model.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Built-in admin support:&lt;/strong&gt; Django has &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/#generic-relations-in-admin&quot;&gt;built-in support for generic foreign keys in the admin&lt;/a&gt;. It can inline, for example, information about the referenced models in the detail page.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Self-contained module:&lt;/strong&gt; There is no direct dependency between the products module and the cart module. This makes this approach ideal for existing projects and pluggable modules.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Can produce inefficient queries:&lt;/strong&gt; The ORM cannot determine in advance what models are referenced by the generic foreign key. This makes it very difficult for it to optimize queries that fetch multiple types of products.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Harder to understand and maintain:&lt;/strong&gt; Generic foreign key eliminates some Django ORM features that require access to specific product models. Accessing information from the product models requires writing more code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Typing requires &lt;code&gt;Protocol&lt;/code&gt;:&lt;/strong&gt; Mypy is unable to provide type checking for generic models. A &lt;code&gt;Protocol&lt;/code&gt; is required.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Generic foreign keys are a great choice for pluggable modules or existing projects. The use of &lt;code&gt;GenericForeignKey&lt;/code&gt; and structural subtyping abstract any direct dependency between the modules.&lt;/p&gt;
&lt;p&gt;In the bookstore example, the book and e-book models can exist in a separate app and new products can be added without changing the cart module. For existing projects, a &lt;code&gt;Cart&lt;/code&gt; module can be added with minimal changes to existing code.&lt;/p&gt;
&lt;p&gt;The patterns presented in this article play nicely together. Using a mixture of patterns, you can eliminate some of the disadvantages and optimize the schema for your use case.&lt;/p&gt;
&lt;p&gt;For example, in the generic foreign key approach, you were unable to get the price of the entire cart quickly. You had to fetch each item separately and aggregate. You can address this specific concern by inlining the price of the product on the &lt;code&gt;Item&lt;/code&gt; model (the sparse model approach). This will allow you to query only the &lt;code&gt;Item&lt;/code&gt; model to get the total price very quickly.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, you started with a small town bookstore and grew it to a big e-commerce website. You tackled different types of problems and adjusted your model to accommodate the changes. You learned that problems such as complex code and difficulty adding new programmers to the team are often symptoms of a larger problem. You learned how to identify these problems and solve them.&lt;/p&gt;
&lt;p&gt;You now know how to plan and implement a polymorphic model using the Django ORM. You&amp;rsquo;re familiar with multiple approaches, and you understand their pros and cons. You&amp;rsquo;re able to analyze your use case and decide on the best course of action.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Python Community Interview With Corey Schafer</title>
      <id>https://realpython.com/interview-corey-schafer/</id>
      <link href="https://realpython.com/interview-corey-schafer/"/>
      <updated>2018-12-31T14:00:00+00:00</updated>
      <summary>Corey Schafer is a full-time content creator publishing regular Python tutorials on YouTube. In this interview, Corey gives his advice for budding YouTubers and content creators. He also has some thoughts about how programming and woodworking are similar!</summary>
      <content type="html">
        &lt;p&gt;For this week&amp;rsquo;s community interview, I am joined by Corey Schafer, of YouTube fame. &lt;/p&gt;
&lt;p&gt;Corey is a full-time content creator publishing regular &lt;a href=&quot;https://www.youtube.com/watch?v=OPUP4ghN9oo&quot;&gt;Python tutorials on YouTube&lt;/a&gt;. In this interview, we talk to Corey about his YouTube channel and his advice for budding YouTubers and content creators, getting his first developer job, and his passion for woodworking.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Welcome to Real Python! We might as well start at the beginning. How’d you get into programming, and when did you start using Python?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3 rounded-circle&quot; src=&quot;https://files.realpython.com/media/corey.62669baa4dba.jpg&quot; width=&quot;864&quot; height=&quot;864&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/corey.62669baa4dba.jpg&amp;amp;w=216&amp;amp;sig=26c64351c6ea562ecf42ac421387058867a3dc97 216w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/corey.62669baa4dba.jpg&amp;amp;w=432&amp;amp;sig=f116094fbdee47b63655f820cd21a18f0009acc1 432w, https://files.realpython.com/media/corey.62669baa4dba.jpg 864w&quot; sizes=&quot;75vw&quot; alt=&quot;Corey Schafer&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Corey:&lt;/strong&gt; Thanks for having me. I actually got started programming a little later in life than most people you&amp;rsquo;ve probably interviewed. I used to be a little self-conscious about getting started later, but now I try to be upfront about it so that others aren&amp;rsquo;t as intimidated if they are just getting started at an &amp;ldquo;older&amp;rdquo; age.&lt;/p&gt;
&lt;p&gt;I suppose I technically got started in college when I began my degree in Computer Science, but even at that time, I wasn&amp;rsquo;t taking it very seriously. I would do enough to pass tests, but I wasn&amp;rsquo;t absorbing any of the information. I definitely wasn&amp;rsquo;t doing any side projects or using coding in any real-world applications.&lt;/p&gt;
&lt;p&gt;It wasn&amp;rsquo;t until my mid-20s that I randomly applied to an internship for NASA at Kennedy Space Center and started to take programming seriously. To my surprise, I was chosen for the internship. Imposter syndrome set in full-force on day 1. I was definitely out of my league.&lt;/p&gt;
&lt;p&gt;But the longer I worked there, the more I realized that these people weren&amp;rsquo;t superhuman. These were people just like me, with the exception that they&amp;rsquo;ve spent much more time putting in the work to master their skill.&lt;/p&gt;
&lt;p&gt;I thought to myself, &amp;ldquo;If they can do it, there&amp;rsquo;s no reason I can&amp;rsquo;t.&amp;rdquo; So I left Cape Canaveral with a newfound motivation to really dive into programming and learn as much as I could. I really didn&amp;rsquo;t feel like I could call myself an actual programmer until my late 20s. I am in my early 30s now, so I feel like I haven&amp;rsquo;t even scratched the surface in terms of the skills I would like the master.&lt;/p&gt;
&lt;p&gt;As far as learning Python, I didn&amp;rsquo;t start using it until about 4 years ago. I was a full-time front-end JavaScript developer at the West Virginia University GIS Technical Center doing some mapping work. We were using Python for some of the backend scripts, and I was assigned to maintain/update some of those. I found that I enjoyed that much more than the front-end JavaScript work and began using Python on a daily basis.&lt;/p&gt;
&lt;p class=&quot;mt-5&quot;&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>
  

</feed>
