<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sebastian Witowski</title>
    <description>The latest articles on Forem by Sebastian Witowski (@switowski).</description>
    <link>https://forem.com/switowski</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F126232%2F228a7744-0641-47fe-a045-860852eeb075.jpg</url>
      <title>Forem: Sebastian Witowski</title>
      <link>https://forem.com/switowski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/switowski"/>
    <language>en</language>
    <item>
      <title>Add Comments To Your Commands</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Thu, 20 Oct 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/add-comments-to-your-commands-2jle</link>
      <guid>https://forem.com/switowski/add-comments-to-your-commands-2jle</guid>
      <description>&lt;p&gt;A quick tip: when you write an important command in shell, put a comment next to it so you can easily find it later or remember what it does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;-m&lt;/span&gt; slow &lt;span class="nt"&gt;--durations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10 &lt;span class="c"&gt;# Run slow tests, return the slowest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8jtoMX1k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5hzkcefzr0gphgspcrz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8jtoMX1k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5hzkcefzr0gphgspcrz.gif" alt="Comic stripe with a programmer pressing arrow up multiple times instead of typing 'ls'" width="650" height="890"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.commitstrip.com/en/2017/02/28/definitely-not-lazy/"&gt;https://www.commitstrip.com/en/2017/02/28/definitely-not-lazy/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I rely heavily on searching commands in history. Something like the guy above, but I usually press &lt;code&gt;ctrl&lt;/code&gt;+&lt;code&gt;r&lt;/code&gt; instead of &lt;code&gt;↑&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem with this approach is that sometimes it's not easy to figure out how to find a specific command. You have to know at least part of the command, which might be difficult if you copied it from somewhere and immediately forgot about it. Or maybe the part that you remember is too generic. If I want to rerun &lt;code&gt;pytest&lt;/code&gt; with a specific set of parameters, I know it starts with &lt;code&gt;pytest&lt;/code&gt;, but that doesn't really narrow it down.&lt;/p&gt;

&lt;p&gt;So I started annotating commands I might want to reuse in the future. Let's say I want to run &lt;code&gt;pytest&lt;/code&gt; on all the tests marked as "slow" and return the slowest ones. I can put a comment right next to the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;-m&lt;/span&gt; slow &lt;span class="nt"&gt;--durations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10 &lt;span class="c"&gt;# Run slow tests, return the slowest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will make it much easier to find this command in the future. It works especially well in combination with a fuzzy finder like the &lt;a href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2qWx-nuX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6o2raqoaz076lyrh28ej.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2qWx-nuX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6o2raqoaz076lyrh28ej.jpg" alt="Searching for a command using fzf" width="880" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A real-world example
&lt;/h2&gt;

&lt;p&gt;I often use this trick in IPython when I want to find some example data in the system. In one of the projects I worked on, we had a database full of objects with numeric IDs. If I wanted to grab an object, I could either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search in the UI for the ID of an object I want to retrieve from the DB. This takes time. I don't want to fire up the UI and do all the filtering to get the ID. So usually, I use the second approach.&lt;/li&gt;
&lt;li&gt;Grab a random object from the DB and hope it has the type I'm looking for. If not, grab a different one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sure, the second approach might take much longer than the first approach. But just like the guy pressing the arrow up 50 times, I kept thinking that &lt;em&gt;"the next object &lt;strong&gt;has&lt;/strong&gt; to be the right one."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I solved that problem by adding comments - each time I grabbed a specific object from the DB, I wrote a comment on what's that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;read_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/Trade/98182379"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Trade X from "Portfolio A"
&lt;/span&gt;&lt;span class="n"&gt;read_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/Trade/18293712"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Trade Y from "Portfolio B"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That way, the next time I wanted to grab an object from Portfolio A, I just needed to hit &lt;code&gt;ctrl&lt;/code&gt;+&lt;code&gt;r&lt;/code&gt; and type "Portfolio A".&lt;/p&gt;

</description>
      <category>cli</category>
    </item>
    <item>
      <title>25 IPython Tips for Your Next Advent of Code</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Wed, 27 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/25-ipython-tips-for-your-next-advent-of-code-2e7</link>
      <guid>https://forem.com/switowski/25-ipython-tips-for-your-next-advent-of-code-2e7</guid>
      <description>&lt;p&gt;I’ve decided to skip last year’s &lt;a href="https://adventofcode.com/"&gt;Advent of Code&lt;/a&gt; edition. Mostly because I didn’t have time, but I also knew that I probably wouldn’t finish it. I’ve never finished any edition. I’m not very good at code katas, and I usually try to brute force them. With AoC, that works for the first ten days, but then the challenges start to get more and more complicated, and adding the @jit decorator to &lt;a href="https://dev.to/blog/easy-speedup-wins-with-numba#how-did-i-find-numba"&gt;speed up my ugly Python code&lt;/a&gt; can only get me so far.&lt;/p&gt;

&lt;p&gt;But one thing that helped me a lot with the previous editions was to use IPython. Solving those problems incrementally is what actually makes it fun. You start by hard-coding the simple example that comes with each task. Then you try to find a solution for this small-scale problem. You try different things, you wrangle with the input data, and after each step, you see the output, so you know if you are getting closer to solving it or not. Once you manage to solve the simple case, you load the actual input data, and you run it just to find out that there were a few corner cases that you missed. It wouldn’t be fun if I had to use a compiled language and write a full program to see the first results.&lt;/p&gt;

&lt;p&gt;This year, instead of doing the “Advent of Code,” I’ve decided to do an “Advent of IPython” on Twitter - for 25 days, &lt;a href="https://twitter.com/SebaWitowski/status/1334427973945012224"&gt;I’ve shared tips&lt;/a&gt; that can help you when you’re solving problems like AoC using IPython. Here is a recap of what you can do.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Display the documentation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findall&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;
&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Docstring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;Return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;all&lt;/span&gt; &lt;span class="n"&gt;non&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;overlapping&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;capturing&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;will&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;tuples&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;
&lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;than&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Empty&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pyenv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;versions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;3.9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s one of my favorite features. You can display the documentation of any function, module, and variable by adding the “?” at the beginning or at the end of it. It’s called “dynamic object introspection,” and I love it because I don’t have to leave the terminal to get the documentation. You can use the built-in &lt;code&gt;help()&lt;/code&gt; function to get this information with the standard Python REPL, but I find the “?” much more readable. It highlights the most important information like the signature and the docstring, and it comes with colors (even though you can’t see them here because my syntax highlighting library doesn’t support IPython)&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Display the source code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pandas&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;

&lt;span class="n"&gt;Init&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;pandas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ForwardRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'ExtensionDtype'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;NoneType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NDFrame&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;"""
    Two-dimensional, size-mutable, potentially heterogeneous tabular data.

    Data structure also contains labeled axes (rows and columns).
    Arithmetic operations align on both row and column labels. Can be
    thought of as a dict-like container for Series objects. The primary
    pandas data structure.

    Parameters
    ----------

... and so on
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you want to see the full source code of a function (or class/module), use two question marks instead (&lt;code&gt;function_name??&lt;/code&gt; or &lt;code&gt;??function_name&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  3. %edit magic function
&lt;/h2&gt;

&lt;p&gt;Sorry for the embedded tweets, but dev.to doesn't support longer GIFs (at least I didn't find a way to embed them including hosting them from my website, giphy, or imgur).&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__video-wrapper"&gt;
        &lt;div class="ltag__twitter-tweet__media--video-preview"&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8GoNg9X9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/tweet_video_thumb/EoTfKfFXEAIdM0S.jpg" alt="unknown tweet media content"&gt;
          &lt;img src="/assets/play-butt.svg" class="ltag__twitter-tweet__play-butt" alt="Play butt"&gt;
        &lt;/div&gt;
        &lt;div class="ltag__twitter-tweet__video"&gt;
          
            
          
        &lt;/div&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--xKYeftfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1087400980185006082/WtuzQSQ2_normal.jpg" alt="Sebastian Witowski profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Sebastian Witowski
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @sebawitowski
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      3. If you want to write a longer function, use "%edit" command.&lt;br&gt;IPython will open a temporary file in your favorite editor (you need to set it with the EDITOR environment variable), and when you save and close it, it will execute that code. 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      09:59 AM - 03 Dec 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1334437070333472770" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WwRENZp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1334437070333472770" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PFD0MJBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1334437070333472770" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6wx1BHu3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;I use it with vim, and it works great when I want to write a bit longer function (with vim I have a lightweight linter, and moving around the code is faster). It’s a nice middle ground when you are too lazy to switch to your code editor to write the whole code, but at the same time, the function that you are writing is a bit too big to write it comfortably in IPython.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Reopen last file with “%edit -p”
&lt;/h2&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__video-wrapper"&gt;
        &lt;div class="ltag__twitter-tweet__media--video-preview"&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mqulmQ7I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/tweet_video_thumb/EoYXZ90XcAAHolB.jpg" alt="unknown tweet media content"&gt;
          &lt;img src="/assets/play-butt.svg" class="ltag__twitter-tweet__play-butt" alt="Play butt"&gt;
        &lt;/div&gt;
        &lt;div class="ltag__twitter-tweet__video"&gt;
          
            
          
        &lt;/div&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--xKYeftfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1087400980185006082/WtuzQSQ2_normal.jpg" alt="Sebastian Witowski profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Sebastian Witowski
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @sebawitowski
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      4. Speaking of the %edit command, you can run "%edit -p" to reopen the same file that you edited last time. This is great to incrementally build some bigger code block or to quickly fix a mistake you made last time. 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      08:43 AM - 04 Dec 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1334780261771464704" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WwRENZp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1334780261771464704" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PFD0MJBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1334780261771464704" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6wx1BHu3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  5. Wildcard search
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;__dir__&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chdir&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;curdir&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fchdir&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listdir&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;makedirs&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pardir&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;removedirs&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rmdir&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scandir&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;supports_dir_fd&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/some/other/dir"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you forget the name of some function, you can combine the dynamic object introspection (the “?”) and a wildcard (the “*”) to perform a wildcard search. For example, I know that the &lt;code&gt;os&lt;/code&gt; module has a function to change the current directory, but I don’t remember its name. I can list all the functions from the &lt;code&gt;os&lt;/code&gt; module, but I’m sure that a function like this must contain “dir” in its name. So I can limit the search and list all the functions from the &lt;code&gt;os&lt;/code&gt; module that contain “dir” in their names.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. post-mortem debugging
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;solver&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;switowski&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;count_trees&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;
     &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;dy&lt;/span&gt;
&lt;span class="o"&gt;--------&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
     &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;

&lt;span class="n"&gt;ipdb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Displaying the documentation is &lt;em&gt;one of&lt;/em&gt; my favorite features, but post-mortem debugging is &lt;strong&gt;my favorite&lt;/strong&gt; feature. After you get an exception, you can run &lt;code&gt;%debug&lt;/code&gt;, and it will start a debugging session for that exception. That’s right! You don’t need to put any breakpoints or run IPython with any special parameters. You just start coding, and &lt;del&gt;if&lt;/del&gt; when an exception happens, you run this command to start debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Start the debugger automatically
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;pdb&lt;/span&gt;
&lt;span class="n"&gt;Automatic&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt; &lt;span class="n"&gt;calling&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;been&lt;/span&gt; &lt;span class="n"&gt;turned&lt;/span&gt; &lt;span class="n"&gt;ON&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;solver&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;switowski&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;count_trees&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;
     &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;dy&lt;/span&gt;
&lt;span class="o"&gt;--------&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
     &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;

&lt;span class="n"&gt;ipdb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;ipdb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;ipdb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you want to start a debugger on every exception automatically, you can run &lt;code&gt;%pdb&lt;/code&gt; to enable the automatic debugger. Run &lt;code&gt;%pdb&lt;/code&gt; again to disable it.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Run shell commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pwd&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;switowski&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;ls&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;al&lt;/span&gt;
&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="n"&gt;drwxr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;switowski&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt; &lt;span class="n"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;drwxr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt; &lt;span class="n"&gt;switowski&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt; &lt;span class="mi"&gt;1760&lt;/span&gt; &lt;span class="n"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;drwxr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;switowski&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt; &lt;span class="mi"&gt;384&lt;/span&gt; &lt;span class="n"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;drwxr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;switowski&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt; &lt;span class="n"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt; &lt;span class="n"&gt;__pycache__&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;switowski&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt; &lt;span class="mi"&gt;344&lt;/span&gt; &lt;span class="n"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;

&lt;span class="c1"&gt;# Node REPL inside IPython? Sure!
&lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;
&lt;span class="n"&gt;Welcome&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt; &lt;span class="n"&gt;v12&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="s"&gt;".help"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello world"&lt;/span&gt;
&lt;span class="n"&gt;undefined&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="s"&gt;'Hello world'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run shell commands without leaving IPython - you just need to prefix it with the exclamation mark. And the most common shell commands like &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;pwd&lt;/code&gt;, &lt;code&gt;cd&lt;/code&gt; will work even without it (of course, unless you have a Python function with the same name).&lt;/p&gt;

&lt;p&gt;I use it mostly to move between folders or to move files around. But you can do all sorts of crazy things - including starting a REPL for a different programming language inside IPython.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Move around the filesystem with %cd
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pwd&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;switowski&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input_files&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;wrong&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;folder&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;switowski&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input_files&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;right_folder&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;switowski&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input_files&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;right_folder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can also move around the filesystem using the &lt;code&gt;%cd&lt;/code&gt; magic command (press Tab to get the autocompletion for the list of available folders). It comes with some additional features - you can bookmark a folder or move a few folders back in the history (run &lt;code&gt;%cd?&lt;/code&gt; to see the list of options).&lt;/p&gt;

&lt;h2&gt;
  
  
  10. %autoreload
&lt;/h2&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__video-wrapper"&gt;
        &lt;div class="ltag__twitter-tweet__media--video-preview"&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GorTemc5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/tweet_video_thumb/EoyQhrJWEAE8Syp.jpg" alt="unknown tweet media content"&gt;
          &lt;img src="/assets/play-butt.svg" class="ltag__twitter-tweet__play-butt" alt="Play butt"&gt;
        &lt;/div&gt;
        &lt;div class="ltag__twitter-tweet__video"&gt;
          
            
          
        &lt;/div&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--xKYeftfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1087400980185006082/WtuzQSQ2_normal.jpg" alt="Sebastian Witowski profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Sebastian Witowski
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @sebawitowski
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      10. Use "%autoreload" to automatically reload any imported function/module before running them.&lt;br&gt;It's great when you want to edit code in one window and run it in IPython without reimporting it every time (more info here: &lt;a href="https://t.co/EDCHgi04Q1"&gt;switowski.com/blog/ipython-a…&lt;/a&gt;). 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      07:23 AM - 10 Dec 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1336934423284101123" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WwRENZp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1336934423284101123" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PFD0MJBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1336934423284101123" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6wx1BHu3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;Use &lt;code&gt;%autoreload&lt;/code&gt; to automatically reload all the imported functions before running them. By default, when you import a function in Python, Python &lt;em&gt;“saves its source code in memory”&lt;/em&gt; (ok, that’s not what actually happens, but for illustration purposes, let’s stick with that oversimplification). When you change the source code of that function, Python won’t notice the change, and it will keep using the outdated version.&lt;/p&gt;

&lt;p&gt;If you are building a function or a module and you want to keep testing the latest version without restarting the IPython (or using the &lt;a href="https://docs.python.org/3/library/importlib.html#importlib.reload"&gt;importlib.reload()&lt;/a&gt;), you can use the &lt;code&gt;%autoreload&lt;/code&gt; magic command. It will always reload the source code before running your functions. If you want to learn more - I wrote a &lt;a href="https://dev.topost_url%202019-10-01-ipython-autoreload"&gt;longer article about it&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Change the verbosity of exceptions
&lt;/h2&gt;

&lt;p&gt;By default, the amount of information in IPython’s exceptions is just right - at least for me. But if you prefer to change that, you can use the &lt;code&gt;%xmode&lt;/code&gt; magic command. It will switch between 4 levels of traceback’s verbosity. Check it out - it’s the same exception, but the traceback gets more and more detailed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimal
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;xmode&lt;/span&gt;
&lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;reporting&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Minimal&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Plain
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;xmode&lt;/span&gt;
&lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;reporting&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Plain&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;ipython-input-6-6f300b4f5987&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"/Users/switowski/workspace/iac/solver.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;
    &lt;span class="n"&gt;sol_part1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;part1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"/Users/switowski/workspace/iac/solver.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;part1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count_trees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"/Users/switowski/workspace/iac/solver.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;count_trees&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nb"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Context (that’s the default setting)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;xmode&lt;/span&gt;
&lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;reporting&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="nb"&gt;IndexError&lt;/span&gt; &lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ipython&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f300b4f5987&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;---------&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
     &lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="n"&gt;vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;--------&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt; &lt;span class="n"&gt;sol_part1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;part1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Part 1: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sol_part1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Part 2: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;part2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sol_part1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;part1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="mi"&gt;14&lt;/span&gt;
     &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;part1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;--------&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count_trees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="mi"&gt;17&lt;/span&gt;
     &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;part2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sol_part1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;count_trees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;
     &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;dy&lt;/span&gt;
&lt;span class="o"&gt;--------&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
     &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt;

&lt;span class="nb"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Verbose (like “Context” but also shows the values of local and global variables)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;xmode&lt;/span&gt;
&lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;reporting&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Verbose&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="nb"&gt;IndexError&lt;/span&gt; &lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ipython&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f300b4f5987&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;---------&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x109312b80&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
     &lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;--------&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt; &lt;span class="n"&gt;part1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solve1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;part1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt;
        &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;solve1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;solve1&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x109f363a0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;'..##.......'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="s"&gt;'.#..#...#.#'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
     &lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Part 1: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;part1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Part 2: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;solve2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;part1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;iac&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;solve1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;'..##.......'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="s"&gt;'.#..#...#.#'&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
     &lt;span class="mi"&gt;14&lt;/span&gt;
     &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;solve1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;--------&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count_trees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;count_trees&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;count_trees&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x109f364c0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;'..##.......'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="s"&gt;'.#..#...#.#'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
     &lt;span class="mi"&gt;17&lt;/span&gt;
     &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;solve2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sol_part1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;so&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;

&lt;span class="nb"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  12. Rerun commands from the previous sessions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;

&lt;span class="c1"&gt;# Restart IPython
&lt;/span&gt;
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rerun&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;Executing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the &lt;code&gt;%rerun ~1/&lt;/code&gt; to rerun all the commands from the previous session. That’s a great way to get you back to the same place where you left IPython. But it has one huge downside - if you had any exception (and I’m pretty sure you did), the execution will stop there. So you have to remove the lines with exceptions manually. If you are using Jupyter Notebooks, there is &lt;a href="https://github.com/jupyter/notebook/pull/2549"&gt;a workaround&lt;/a&gt; that allows you to tag a notebook cell as “raising an exception.” If you rerun it, IPython will ignore this exception. It’s not a perfect solution, and an option to ignore exceptions during the %rerun command would be much better.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Execute some code at startup
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TcnRvZwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2021-01-27-ipython-startup.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TcnRvZwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2021-01-27-ipython-startup.gif" alt="Startup folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to execute some code each time you start IPython, just create a new file inside the “startup” folder (&lt;code&gt;~/.ipython/profile_default/startup/&lt;/code&gt;) and add your code there. IPython will automatically execute any files it finds in this folder. It’s great if you want to import some modules that you use all the time, but if you put too much code there, the startup time of IPython will be slower.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. Use different profiles
&lt;/h2&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__video-wrapper"&gt;
        &lt;div class="ltag__twitter-tweet__media--video-preview"&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LP5o9jTF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/tweet_video_thumb/EpLyjbkXMAAf2Ao.jpg" alt="unknown tweet media content"&gt;
          &lt;img src="/assets/play-butt.svg" class="ltag__twitter-tweet__play-butt" alt="Play butt"&gt;
        &lt;/div&gt;
        &lt;div class="ltag__twitter-tweet__video"&gt;
          
            
          
        &lt;/div&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--xKYeftfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1087400980185006082/WtuzQSQ2_normal.jpg" alt="Sebastian Witowski profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Sebastian Witowski
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @sebawitowski
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      14. You can create a new profile with different settings. For example, you can create a profile just for debugging with verbose exceptions and plenty of debugging/profiling libraries automatically imported. 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      08:21 AM - 14 Dec 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1338398758489513985" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WwRENZp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1338398758489513985" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PFD0MJBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1338398758489513985" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6wx1BHu3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;Maybe you have a set of modules that you want to import and settings to set in a specific situation. For example, when debugging/profiling, you want to set the exceptions to the verbose mode and import some profiling libraries. Don’t put that into the default profile because you don’t debug or profile your code all the time. Create a new profile and put your debugging settings inside. Profiles are like different user accounts for IPython - each of them has its own configuration file and startup folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  15. Output from the previous commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;499999500000&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;the_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;the_sum&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;499999500000&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;_1&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;499999500000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you forgot to assign an expression to a variable, use &lt;code&gt;var = _&lt;/code&gt;. &lt;code&gt;_&lt;/code&gt; stores the output of the last command (this also works in the standard Python REPL). The results of all the previous commands are stored in variables &lt;code&gt;_1&lt;/code&gt; (output from the first command), &lt;code&gt;_2&lt;/code&gt; (output from the second command), etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  16. Edit any function or module
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hjx_txIh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2021-01-27-ipython-edit-any-function.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hjx_txIh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2021-01-27-ipython-edit-any-function.gif" alt="Editing any function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;%edit&lt;/code&gt; to edit any Python function. And I really mean &lt;strong&gt;ANY&lt;/strong&gt; function - functions from your code, from packages installed with pip, or even the built-in ones. You don’t even need to know in which file that function is located. Just specify the name (you have to import it first), and IPython will find it for you.&lt;/p&gt;

&lt;p&gt;In the above example, I’m breaking the built-in &lt;code&gt;randint()&lt;/code&gt; function by always returning 42.&lt;/p&gt;

&lt;h2&gt;
  
  
  17. Share your code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;welcome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Welcome to my gist"&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;welcome&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s"&gt;'Welcome to my gist'&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;pastebin&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s"&gt;'http://dpaste.com/8QA86F776'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to share your code with someone, use the &lt;code&gt;%pastebin&lt;/code&gt; command and specify which lines you want to share. IPython will create a pastebin (something similar to &lt;a href="https://gist.github.com/"&gt;GitHub gist&lt;/a&gt;), paste selected lines, and return a link that you can send to someone. Just keep in mind that this snippet will expire in 7 days.&lt;/p&gt;

&lt;h2&gt;
  
  
  18. Use IPython as your debugger
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JiIc8d44--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2021-01-27-ipython-debugger.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JiIc8d44--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2021-01-27-ipython-debugger.gif" alt="IPython as a debugger"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maybe some of the tips that I’ve shared convinced you that IPython is actually pretty cool. If that’s the case, you can use it not only as a REPL (the interactive Python shell) but also as a debugger. IPython comes with “ipdb” - it’s like the built-in Python debugger “pdb”, but with some IPython’s features on top of it (syntax highlighting, autocompletion, etc.)&lt;/p&gt;

&lt;p&gt;You can use ipdb with your breakpoint statements by setting the &lt;code&gt;PYTHONBREAKPOINT&lt;/code&gt; environment variable - it controls what happens when you call &lt;code&gt;breakpoint()&lt;/code&gt; in your code. This trick requires using Python 3.7 or higher (that’s when the &lt;code&gt;breakpoint()&lt;/code&gt; statement was introduced).&lt;/p&gt;

&lt;h2&gt;
  
  
  19. Execute code written in another language
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%%&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="n"&gt;upto&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="n"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"Fizz"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"Buzz"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="n"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;Fizz&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;Buzz&lt;/span&gt;
&lt;span class="n"&gt;Fizz&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="n"&gt;Fizz&lt;/span&gt;
&lt;span class="n"&gt;Buzz&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;
&lt;span class="n"&gt;Fizz&lt;/span&gt;
&lt;span class="mi"&gt;13&lt;/span&gt;
&lt;span class="mi"&gt;14&lt;/span&gt;
&lt;span class="n"&gt;FizzBuzz&lt;/span&gt;
&lt;span class="mi"&gt;16&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s say you want to execute some code written in another language without leaving IPython. You might be surprised to see that IPython supports Ruby, Bash, or JavaScript out of the box. And even more languages can be supported when you install additional kernels!&lt;/p&gt;

&lt;p&gt;Just type &lt;code&gt;%%ruby&lt;/code&gt;, write some Ruby code, and press Enter twice, and IPython will run it with no problem. It also works with Python2 (&lt;code&gt;%%python2&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  20. Store variables between sessions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;Stored&lt;/span&gt; &lt;span class="s"&gt;'a'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Restart IPython
&lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IPython uses SQLite for some lightweight storage between sessions. That’s where it saves the history of your previous sessions. But you can use it to store your own data. For example, with the &lt;code&gt;%store&lt;/code&gt; magic command, you can save variables in IPython’s database and restore them in another session using &lt;code&gt;%store -r&lt;/code&gt;. You can also set the &lt;code&gt;c.StoreMagics.autorestore = True&lt;/code&gt; in the configuration file to automatically restore all the variables from the database when you start IPython.&lt;/p&gt;

&lt;h2&gt;
  
  
  21. Save session to a file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt; &lt;span class="n"&gt;were&lt;/span&gt; &lt;span class="n"&gt;written&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="sb"&gt;`filename.py`&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can save your IPython session to a file with the &lt;code&gt;%save&lt;/code&gt; command. That’s quite useful when you have some working code and you want to continue editing it with your text editor. Instead of manually copying and pasting lines to your code editor, you can dump the whole IPython session and then remove unwanted lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  22. Clean up “&amp;gt;” symbols and fix indentation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Clipboard content:
# &amp;gt;def greet(name):
# &amp;gt; print(f"Hello {name}")
&lt;/span&gt;
&lt;span class="c1"&gt;# Just pasting the code won't work
&lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Hello &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;ipython-input-1-a7538fc939af&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;^&lt;/span&gt;
&lt;span class="nb"&gt;SyntaxError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;syntax&lt;/span&gt;

&lt;span class="c1"&gt;# But using %paste works
&lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;paste&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Hello &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;## -- End pasted text --
&lt;/span&gt;
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sebastian"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;Sebastian&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to clean up incorrect indentation or “&amp;gt;” symbols (for example, when you copy the code from a git diff, docstring, or an email), instead of doing it manually, copy the code and run &lt;code&gt;%paste&lt;/code&gt;. IPython will paste the code from your clipboard, fix the indentation, and remove the “&amp;gt;” symbols (although it sometimes doesn’t work properly).&lt;/p&gt;

&lt;h2&gt;
  
  
  23. List all the variables
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Sebastian"&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;squares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;squares_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;whos&lt;/span&gt;
&lt;span class="n"&gt;Variable&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;
&lt;span class="o"&gt;----------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="n"&gt;Sebastian&lt;/span&gt;
&lt;span class="n"&gt;say_hello&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;say_hello&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x111b60a60&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;squares&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;squares_sum&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="mi"&gt;328350&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get a list of all the variables from the current session (nicely formatted, with information about their type and the data they store) with the &lt;code&gt;%whos&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  24. Use asynchronous functions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hi"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bye"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;

&lt;span class="c1"&gt;# The following code would fail in the standard Python REPL
# because we can't call await outside of an async function
&lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;Hi&lt;/span&gt;
&lt;span class="n"&gt;Hi&lt;/span&gt;
&lt;span class="n"&gt;Hi&lt;/span&gt;
&lt;span class="n"&gt;Bye&lt;/span&gt;
&lt;span class="n"&gt;Bye&lt;/span&gt;
&lt;span class="n"&gt;Bye&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can speed up your code with asynchronous functions. But the thing about asynchronous code is that you need to start an event loop to call them. However, IPython comes with its own event loop! And with that, you can await asynchronous functions just like you would call a standard, synchronous one.&lt;/p&gt;

&lt;h2&gt;
  
  
  25. IPython scripts
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;file1.py file2.py file3.py file4.py wishes.ipy

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;wishes.ipy
files &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="c"&gt;# Run all the files with .py suffix&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;file &lt;span class="k"&gt;in &lt;/span&gt;files:
    &lt;span class="k"&gt;if &lt;/span&gt;file.endswith&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;".py"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:
        %run &lt;span class="nv"&gt;$file&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;ipython wishes.ipy
Have a
Very Merry
Christmas!
🎄🎄🎄🎄🎄🎄
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can execute files containing IPython-specific code (shell commands prefixed with &lt;code&gt;!&lt;/code&gt; or magic methods prefixed with &lt;code&gt;%&lt;/code&gt;). Just save the file with the “.ipy” extension and then pass it to the &lt;code&gt;ipython&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;If you have been reading my blog for a bit, you probably already realize that IPython is one of my favorite Python tools. It’s an excellent choice for solving code challenges like the Advent of Code, and it has a lot of cool tricks that can help you. Leave a comment if you know some other cool tricks that you want to share!&lt;/p&gt;




&lt;p&gt;Photo by Valeria Vinnik from: &lt;a href="https://www.pexels.com/photo/ball-blur-branch-celebration-246351/"&gt;Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ipython</category>
    </item>
    <item>
      <title>Sorting Lists in Python</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Thu, 22 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/sorting-lists-in-python-5a7b</link>
      <guid>https://forem.com/switowski/sorting-lists-in-python-5a7b</guid>
      <description>&lt;p&gt;There are at least two common ways to sort lists in Python:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With &lt;a href="https://docs.python.org/3/library/functions.html#sorted"&gt;sorted&lt;/a&gt; function that returns a new list&lt;/li&gt;
&lt;li&gt;With &lt;a href="https://docs.python.org/3/library/stdtypes.html#list.sort"&gt;list.sort&lt;/a&gt; method that modifies list in place&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which one is faster? Let's find out!&lt;/p&gt;

&lt;h2&gt;
  
  
  sorted() vs list.sort()
&lt;/h2&gt;

&lt;p&gt;I will start with a list of 1 000 000 randomly shuffled integers. Later on, I will also check if the order matters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# sorting.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;

&lt;span class="c1"&gt;# List of 1 000 000 integers randomly shuffled
&lt;/span&gt;&lt;span class="n"&gt;MILLION_RANDOM_NUMBERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_sort&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;random_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MILLION_RANDOM_NUMBERS&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;random_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_sorted&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;random_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MILLION_RANDOM_NUMBERS&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from sorting import test_sort"&lt;/span&gt; &lt;span class="s2"&gt;"test_sort()"&lt;/span&gt;
1 loop, best of 5: 352 msec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from sorting import test_sorted"&lt;/span&gt; &lt;span class="s2"&gt;"test_sorted()"&lt;/span&gt;
1 loop, best of 5: 385 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sorted&lt;/code&gt; is less than 10% slower (385/352≈1.094). Since we only run one loop, the exact numbers are not very reliable. I have rerun the same tests a couple more times, and the results were slightly different each time. &lt;code&gt;sort&lt;/code&gt; took around 345-355 msec and &lt;code&gt;sorted&lt;/code&gt; took around 379-394 msec (but it was always slower than &lt;code&gt;sort&lt;/code&gt;). This difference comes mostly from the fact that &lt;code&gt;sorted&lt;/code&gt; creates a new list.&lt;/p&gt;

&lt;p&gt;Why do I make a copy of the original list in each test? Well, in the original version of this article, I forgot to do this, and I ended up with completely wrong benchmarks. &lt;code&gt;sort&lt;/code&gt; was running on a sorted list (because the first iteration of &lt;code&gt;timeit&lt;/code&gt; sorted it), and &lt;code&gt;sorted&lt;/code&gt; was running on a random list. You can see the whole explanation in the &lt;a href="https://switowski.com/blog/sorting-lists"&gt;original post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial order matters
&lt;/h2&gt;

&lt;p&gt;What happens when our initial list is already sorted?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from sorting import test_sort"&lt;/span&gt; &lt;span class="s2"&gt;"test_sort()"&lt;/span&gt;
20 loops, best of 5: 12.1 msec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from sorting import test_sorted"&lt;/span&gt; &lt;span class="s2"&gt;"test_sorted()"&lt;/span&gt;
20 loops, best of 5: 16.6 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, sorting takes much less time and the difference between &lt;code&gt;sort&lt;/code&gt; and &lt;code&gt;sorted&lt;/code&gt; grows to 37% (16.6/12.1≈1.372). Why is &lt;code&gt;sorted&lt;/code&gt; 37% slower this time? Well, creating a new list takes the same amount of time as before. And since the time spent on sorting has shrunk, the impact of creating that new list got bigger.&lt;/p&gt;

&lt;p&gt;And if we try to sort a list of 1 000 000 numbers ordered in descending order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;DESCENDING_MILLION_NUMBERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from sorting import test_sort"&lt;/span&gt; &lt;span class="s2"&gt;"test_sort()"&lt;/span&gt;
20 loops, best of 5: 11.7 msec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from sorting import test_sorted"&lt;/span&gt; &lt;span class="s2"&gt;"test_sorted()"&lt;/span&gt;
20 loops, best of 5: 18.1 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are almost identical as before. The sorting algorithm is clever enough to optimize the sorting process for a descending list.&lt;/p&gt;

&lt;p&gt;For our last test, let’s try to sort 1 000 000 numbers where 100 000 elements are shuffled, and the rest are ordered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 10% of numbers are random
&lt;/span&gt;&lt;span class="n"&gt;MILLION_SLIGHTLY_RANDOM_NUMBERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;900_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from sorting import test_sort"&lt;/span&gt; &lt;span class="s2"&gt;"test_sort()"&lt;/span&gt;
5 loops, best of 5: 61.2 msec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from sorting import test_sorted"&lt;/span&gt; &lt;span class="s2"&gt;"test_sorted()"&lt;/span&gt;
5 loops, best of 5: 71 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both functions get slower as the input list becomes more scrambled.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;list.sort()&lt;/code&gt; is my preferred way of sorting lists - it saves some time (and memory) by not creating a new list. But that's a double-edged sword! Sometimes you might accidentally overwrite the initial list without realizing it (as I did with my initial benchmarks 😅). So, if you want to preserve the initial list's order, you have to use &lt;code&gt;sorted&lt;/code&gt; instead. And &lt;code&gt;sorted&lt;/code&gt; can be used with any iterable, while &lt;code&gt;sort&lt;/code&gt; &lt;strong&gt;only works with lists&lt;/strong&gt;. If you want to sort a set, then sorted is your only solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sort&lt;/code&gt; is slightly faster than &lt;code&gt;sorted&lt;/code&gt;, because it doesn't create a new list. But you might still stick with &lt;code&gt;sorted&lt;/code&gt; if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don't want to modify the original list. &lt;code&gt;sort&lt;/code&gt; performs sorting in-place, so you can't use it here.&lt;/li&gt;
&lt;li&gt;You need to sort something else than a list. &lt;code&gt;sort&lt;/code&gt; is only defined on lists, so if you want to sort a set or any other collection of items, you have to use &lt;code&gt;sorted&lt;/code&gt; instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to learn more, the &lt;a href="https://docs.python.org/3/howto/sorting.html"&gt;Sorting HOW TO&lt;/a&gt; guide from Python documentation contains a lot of useful information.&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>For Loop vs. List Comprehension</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Thu, 15 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/for-loop-vs-list-comprehension-4g93</link>
      <guid>https://forem.com/switowski/for-loop-vs-list-comprehension-4g93</guid>
      <description>&lt;p&gt;Many simple "for loops" in Python can be replaced with list comprehensions. You can often hear that list comprehension is &lt;em&gt;"more Pythonic"&lt;/em&gt; (almost as if there was a scale for comparing how &lt;em&gt;Pythonic&lt;/em&gt; something is, compared to something else 😉). In this article, I will compare their performance and discuss when a list comprehension is a good idea, and when it's not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filter a list with a "for loop"
&lt;/h2&gt;

&lt;p&gt;Let's use a simple scenario for a loop operation - we have a list of numbers, and we want to remove the odd ones. One important thing to keep in mind is that we can't remove items from a list as we iterate over it. Instead, we have to create a new one containing only the even numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# filter_list.py
&lt;/span&gt;
&lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;for_loop&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;if not element % 2&lt;/code&gt; is equivalent to &lt;code&gt;if element % 2 == 0&lt;/code&gt;, but it's slightly faster. I will write a separate article about comparing boolean values soon.&lt;/p&gt;

&lt;p&gt;Let's measure the execution time of this function. I'm using &lt;strong&gt;Python 3.8&lt;/strong&gt; for benchmarks (you can read about the whole setup in the &lt;a href="https://switowski.com/blog/writing-faster-python-intro"&gt;Introduction article on my blog&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from filter_list import for_loop"&lt;/span&gt; &lt;span class="s2"&gt;"for_loop()"&lt;/span&gt;
5 loops, best of 5: 65.4 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes 65 milliseconds to filter a list of one million elements. How fast will a list comprehension deal with the same task?&lt;/p&gt;

&lt;h2&gt;
  
  
  Filter a list with list comprehension
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# filter_list.py
&lt;/span&gt;
&lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_comprehension&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from filter_list import list_comprehension"&lt;/span&gt; &lt;span class="s2"&gt;"list_comprehension()"&lt;/span&gt;
5 loops, best of 5: 44.5 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"For loop" is around 50% slower than a list comprehension (65.4/44.5≈1.47). And we just &lt;strong&gt;reduced five lines of code to one line&lt;/strong&gt;! Cleaner and faster code? Great!&lt;/p&gt;

&lt;p&gt;Can we make it better?&lt;/p&gt;

&lt;h2&gt;
  
  
  Filter a list with the "filter" function
&lt;/h2&gt;

&lt;p&gt;Python has a built-in &lt;a href="https://docs.python.org/3/library/functions.html#filter"&gt;filter&lt;/a&gt; function for filtering collections of elements. This sounds like a perfect use case for our problem, so let's see how fast it will be.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# filter_list.py
&lt;/span&gt;
&lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;filter_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from filter_list import filter_function"&lt;/span&gt; &lt;span class="s2"&gt;"filter_function()"&lt;/span&gt;
1000000 loops, best of 5: 284 nsec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;284 nanoseconds?! That's suspiciously fast! It turns out that the filter function returns an &lt;strong&gt;iterator&lt;/strong&gt;. It doesn't immediately go over one million elements, but it will return the next value when we ask for it. To get all the results at once, we can convert this iterator to a list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# filter_list.py
&lt;/span&gt;
&lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;filter_return_list&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from filter_list import filter_return_list"&lt;/span&gt; &lt;span class="s2"&gt;"filter_return_list()"&lt;/span&gt;
2 loops, best of 5: 104 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, its performance is not so great anymore. It's 133% slower than the list comprehension (104/44.5≈2.337) and 60% slower than the "for loop" (104/65.4≈1.590).&lt;/p&gt;

&lt;p&gt;While, in this case, it's not the best solution, an iterator is an excellent alternative to a list comprehension when we don't need to have all the results at once. If it turns out that we only need to get a few elements from the filtered list, an iterator will be a few orders of magnitude faster than other "non-lazy" solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  More than one operation in the loop
&lt;/h2&gt;

&lt;p&gt;List comprehensions are often faster and easier to read, but they have one significant limitation. What happens if you want to execute more than one simple instruction? List comprehension can't accept multiple statements (without sacrificing readability). But in many cases, you can wrap those multiple statements in a function.&lt;/p&gt;

&lt;p&gt;Let's use a slightly modified version of the famous "Fizz Buzz" program as an example. We want to iterate over a list of elements and for each of them return:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"fizzbuzz" if the number can be divided by 3 and 5&lt;/li&gt;
&lt;li&gt;"fizz" if the number can be divided by 3&lt;/li&gt;
&lt;li&gt;"buzz" if the number can be divided by 5&lt;/li&gt;
&lt;li&gt;the number itself, if it can't be divided by 3 or 5&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a simple solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# filter_list.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fizz_buzz&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'fizzbuzz'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'fizz'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'buzz'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the list comprehension equivalent of the fizz_buzz():&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'fizzbuzz'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;'fizz'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;'buzz'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not easy to read - at least for me. It gets better if we split it into multiple lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"fizzbuzz"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"fizz"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"buzz"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if I see a list comprehension that spans multiple lines, I try to refactor it. We can extract the "if" statements into a separate function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# filter_list.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'fizzbuzz'&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'fizz'&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'buzz'&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fizz_buzz2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's trivial to turn it into a list comprehension. And we get the additional benefit of a nice separation of logic into a function that does the "fizz buzz" check and a function that actually iterates over a list of numbers and applies the "fizz buzz" transformation.&lt;/p&gt;

&lt;p&gt;Here is the improved list comprehension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fizz_buzz2_comprehension&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's compare all three versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from filter_list import fizz_buzz"&lt;/span&gt; &lt;span class="s2"&gt;"fizz_buzz()"&lt;/span&gt;
2 loops, best of 5: 191 msec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from filter_list import fizz_buzz2"&lt;/span&gt; &lt;span class="s2"&gt;"fizz_buzz2()"&lt;/span&gt;
1 loop, best of 5: 285 msec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from filter_list import fizz_buzz2_comprehension"&lt;/span&gt; &lt;span class="s2"&gt;"fizz_buzz2_comprehension()"&lt;/span&gt;
1 loop, best of 5: 224 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extracting a separate function adds some overhead. List comprehension with a separate &lt;code&gt;transform()&lt;/code&gt; function is around 17% slower than the initial "for loop"-based version (224/191≈1.173). But it's much more readable, so I prefer it over the other solutions.&lt;/p&gt;

&lt;p&gt;And, if you are curious, the one-line list comprehension mentioned before is the fastest solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fizz_buzz_comprehension&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s"&gt;"fizzbuzz"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"fizz"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"buzz"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from filter_list import fizz_buzz_comprehension"&lt;/span&gt; &lt;span class="s2"&gt;"fizz_buzz_comprehension()"&lt;/span&gt;
2 loops, best of 5: 147 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fastest, but also harder to read. If you run this code through a code formatter like &lt;a href="https://github.com/psf/black"&gt;black&lt;/a&gt; (which is a common practice in many projects), it will further &lt;em&gt;obfuscate&lt;/em&gt; this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"fizzbuzz"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"fizz"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"buzz"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MILLION_NUMBERS&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is nothing wrong with black here - we are simply putting too much logic inside the list comprehension. If I had to say what the above code does, it would take me much longer to figure it out than if I had two separate functions. Saving a few hundred milliseconds of execution time and adding a few seconds of reading time doesn't sound like a good trade-off 😉.&lt;/p&gt;

&lt;p&gt;Clever one-liners can impress some recruiters during code interviews. But in real life, separating logic into different functions makes it much easier to read and document your code. And, &lt;a href="https://www.goodreads.com/quotes/835238-indeed-the-ratio-of-time-spent-reading-versus-writing-is"&gt;statistically&lt;/a&gt;, we read more code than we write.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;List comprehensions are often not only more readable but also faster than using "for loops." They can simplify your code, but if you put too much logic inside, they will instead become harder to read and understand.&lt;/p&gt;

&lt;p&gt;Even though list comprehensions are popular in Python, they have a specific use case: when you want to perform some operations on a list and return another list. And they have limitations - you can't &lt;code&gt;break&lt;/code&gt; out of a list comprehension or put comments inside. In many cases, "for loops" will be your only choice.&lt;/p&gt;

&lt;p&gt;I only scratched the surface of how useful list comprehension (or any other type of "comprehension" in Python) can be. If you want to learn more, Trey Hunner has many excellent articles and talks on this subject (for example, &lt;a href="https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/"&gt;this one for beginners&lt;/a&gt;).&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Should We Still Use OrderedDict in Python?</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Thu, 08 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/should-we-still-use-ordereddict-in-python-3npl</link>
      <guid>https://forem.com/switowski/should-we-still-use-ordereddict-in-python-3npl</guid>
      <description>&lt;p&gt;If you worked with Python 2 or an early version of Python 3, you probably remember that, in the past, dictionaries were not ordered. If you wanted to have a dictionary that preserved the insertion order, the go-to solution was to use &lt;a href="https://docs.python.org/3/library/collections.html#collections.OrderedDict"&gt;OrderedDict&lt;/a&gt; from the collections module.&lt;/p&gt;

&lt;p&gt;In Python 3.6, dictionaries were redesigned to improve their performance (their memory usage was decreased by around 20-25%). This change had an interesting side-effect - &lt;strong&gt;dictionaries became ordered&lt;/strong&gt; (although this order was &lt;a href="https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-compactdict"&gt;not officially guaranteed&lt;/a&gt;). "Not officially guaranteed" means that it was just an implementation detail that could be removed in the future Python releases.&lt;/p&gt;

&lt;p&gt;But starting from Python 3.7, the insertion-order preservation has been guaranteed in the language specification. If you started your journey with Python 3.7 or a newer version, you probably don't know the world where you need a separate data structure to preserve the insertion order in a dictionary.&lt;/p&gt;

&lt;p&gt;So if there is no need to use the OrderedDict, why is it still included in the collections module? Maybe it's more efficient? Let's find out!&lt;/p&gt;

&lt;h2&gt;
  
  
  OrderedDict vs dict
&lt;/h2&gt;

&lt;p&gt;For my benchmarks, I will perform some typical dictionary operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a dictionary of 100 elements&lt;/li&gt;
&lt;li&gt;Add a new item&lt;/li&gt;
&lt;li&gt;Check if an item exists in a dictionary&lt;/li&gt;
&lt;li&gt;Grab an existing and nonexistent item with the &lt;code&gt;get&lt;/code&gt; method&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To simplify the code, I wrap steps 2-4 in a function that accepts a dictionary (or OrderedDictionary) as an argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# dictionaries.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform_operations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'goodbye'&lt;/span&gt;
    &lt;span class="n"&gt;is_50_included&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt;
    &lt;span class="n"&gt;item_20&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;nonexistent_item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ordereddict&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromkeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;'hello world'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;perform_operations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;standard_dict&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromkeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;'hello world'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;perform_operations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's compare both functions. I run my benchmarks under &lt;strong&gt;Python 3.8&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from dictionaries import ordereddict"&lt;/span&gt; &lt;span class="s2"&gt;"ordereddict()"&lt;/span&gt;
50000 loops, best of 5: 8.6 usec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from dictionaries import standard_dict"&lt;/span&gt; &lt;span class="s2"&gt;"standard_dict()"&lt;/span&gt;
50000 loops, best of 5: 4.7 usec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OrderedDict is over 80% slower than the standard Python dictionary (8.6/4.7≈1.83).&lt;/p&gt;

&lt;p&gt;What happens if the dictionary size grows to 10 000 elements?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# dictionaries2.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform_operations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'goodbye'&lt;/span&gt;
    &lt;span class="n"&gt;is_5000_included&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt;
    &lt;span class="n"&gt;item_2000&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;nonexistent_item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ordereddict&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromkeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;'hello world'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;perform_operations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;standard_dict&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromkeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;'hello world'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;perform_operations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from dictionaries import ordereddict"&lt;/span&gt; &lt;span class="s2"&gt;"ordereddict()"&lt;/span&gt;
200 loops, best of 5: 1.07 msec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from dictionaries import standard_dict"&lt;/span&gt; &lt;span class="s2"&gt;"standard_dict()"&lt;/span&gt;
500 loops, best of 5: 547 usec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After increasing the dictionary size by 100x times, the difference between both functions stays the same. OrderedDict still takes almost twice as long to perform the same operations as a standard Python dictionary.&lt;/p&gt;

&lt;p&gt;There is no point in testing even bigger dictionaries. If you need a really big dictionary, you should use more efficient data structures from the Numpy or Pandas libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use OrderedDict?
&lt;/h2&gt;

&lt;p&gt;If the OrderedDict is slower, why would you want to use it? I can think of at least two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are still using a Python version that doesn't guarantee the order in dictionaries (pre 3.6). In this case, you don't have a choice.&lt;/li&gt;
&lt;li&gt;You want to use additional features that OrderedDict offers. For example, it can be reversed. If you try to run &lt;a href="https://docs.python.org/3/library/functions.html#reversed"&gt;reversed()&lt;/a&gt; function on a standard dictionary, you will get an error, but OrderedDict will nicely return a reversed version of itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to stay up to date on Python changes?
&lt;/h2&gt;

&lt;p&gt;If you are using one of the latest versions of Python, dictionaries are ordered by default. But it's easy to miss changes like this, especially if you upgrade Python version by a few releases at once, and you don't read the release notes carefully. I usually read some blog posts when there is a new version of Python coming out (there are plenty of blog posts around that time), so I catch the essential updates.&lt;/p&gt;

&lt;p&gt;The best source of information is the official documentation. Unlike a lot of documentation that I have seen in my life, the &lt;a href="https://docs.python.org/3/whatsnew/index.html"&gt;"What's New in Python 3"&lt;/a&gt; page is written in a very approachable language. It's easy to read and grasp the most significant changes. If you haven't done it yet, go check it out. I reread it a few days ago, and I was surprised how many features I forgot about!&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Easy Speedup Wins With Numba</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Thu, 01 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/easy-speedup-wins-with-numba-4aod</link>
      <guid>https://forem.com/switowski/easy-speedup-wins-with-numba-4aod</guid>
      <description>&lt;p&gt;If you have functions that do a lot of mathematical operations, use NumPy or rely heavily on loops, then there is a way to speed them up significantly with one line of code. Ok, two lines if you count the import.&lt;/p&gt;

&lt;h2&gt;
  
  
  Numba and the @jit decorator
&lt;/h2&gt;

&lt;p&gt;Meet &lt;a href="https://numba.pydata.org/"&gt;Numba&lt;/a&gt; and its &lt;a href="https://numba.pydata.org/numba-doc/dev/user/jit.html"&gt;@jit&lt;/a&gt; decorator. It changes how your code is compiled, often improving its performance. You don't have to install any special tools (just the &lt;code&gt;numba&lt;/code&gt; pip package), you don't have to tweak any parameters. All you have to do is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the &lt;code&gt;@jit&lt;/code&gt; decorator to a function&lt;/li&gt;
&lt;li&gt;Check if it's faster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see an example of code before and after applying &lt;code&gt;Numba&lt;/code&gt;'s optimization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# numba_testing.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;math&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Bunch of dummy math operations
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only purpose of this code is to do some calculations and to "be slow." Let's see how slow (benchmarks are done with &lt;strong&gt;Python 3.8&lt;/strong&gt; - I describe the whole setup in the &lt;a href="https://switowski.com/blog/writing-faster-python-intro"&gt;Introduction article on my blog&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from numba_testing import compute"&lt;/span&gt; &lt;span class="s2"&gt;"compute()"&lt;/span&gt;
1 loop, best of 5: 217 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we add &lt;code&gt;@jit&lt;/code&gt; to our code. The body of the function stays the same, and the only difference is the decorator. Don't forget to install Numba package with pip (&lt;code&gt;pip install numba&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# numba_testing.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;math&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numba&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jit&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;jit&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_jit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Bunch of dummy math operations
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's measure the execution time once more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from numba_testing import compute_jit"&lt;/span&gt; &lt;span class="s2"&gt;"compute_jit()"&lt;/span&gt;
200 loops, best of 5: 1.76 msec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using @jit decorator gave us a &lt;strong&gt;120x speedup&lt;/strong&gt; (217 / 1.76 = 123.295)! That's a huge improvement for such a simple change!&lt;/p&gt;

&lt;h2&gt;
  
  
  Other features of Numba
&lt;/h2&gt;

&lt;p&gt;@jit is the most common decorator from the Numba library, but there are others that you can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@njit - alias for @jit(nopython=True). In &lt;code&gt;nopython&lt;/code&gt; mode, Numba tries to run your code without using the Python interpreter at all. It can lead to even bigger speed improvements, but it's also possible that the compilation will fail in this mode.&lt;/li&gt;
&lt;li&gt;@vectorize and @guvectorize - produces &lt;code&gt;ufunc&lt;/code&gt; and generalized &lt;code&gt;ufunc&lt;/code&gt; used in NumPy.&lt;/li&gt;
&lt;li&gt;@jitclass - can be used to decorate the whole class.&lt;/li&gt;
&lt;li&gt;@cfunc - declares a function to be used as a native callback (from C or C++ code).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also advanced features that let you, for example, run your code on GPU with @cuda.jit. This doesn't work out of the box, but it might be worth the effort for some very computational-heavy operations.&lt;/p&gt;

&lt;p&gt;Numba has plenty of configuration options that will further improve your code's execution time if you know what you are doing. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disable GIL (&lt;a href="https://docs.python.org/3/glossary.html#term-global-interpreter-lock"&gt;Global Interpreter Lock&lt;/a&gt;) with &lt;code&gt;nogil&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cache results with &lt;code&gt;cache&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Automatically parallelize functions with &lt;code&gt;parallel&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the &lt;a href="https://numba.pydata.org/numba-doc/latest/index.html"&gt;documentation&lt;/a&gt; to see what you can do. And to see more real-life examples (like computing the Black-Scholes model or the Lennard-Jones potential), visit the &lt;a href="https://numba.pydata.org/numba-examples/index.html"&gt;Numba Examples&lt;/a&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Numba&lt;/code&gt; is a great library that can significantly speed up your programs with minimal effort. Given that it takes less than a minute to install and decorate some slow functions, it's one of the first solutions that you can check when you want to quickly improve your code (without rewriting it).&lt;/p&gt;

&lt;p&gt;It works best if your code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses NumPy a lot&lt;/li&gt;
&lt;li&gt;Performs plenty of mathematical operations&lt;/li&gt;
&lt;li&gt;Performs operations is a loop&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How did I find Numba?
&lt;/h2&gt;

&lt;p&gt;I first learned about Numba when I was doing code challenges from the &lt;a href="https://adventofcode.com/"&gt;Advent of Code&lt;/a&gt; a few years ago. I wrote a pretty terrible algorithm, left it running, and went for lunch. When I came back after one hour, my program wasn't even 10% done. I stopped it, added the &lt;code&gt;@jit&lt;/code&gt; decorator to the main function, rerun it, and I had the results in under one minute! Fantastic improvement with almost no work!  &lt;/p&gt;

&lt;p&gt;This story doesn't mean that it's ok to write sloppy code, and then use hacks to speed it up 😉. But sometimes you just need to make some one-off calculations. You don't want to spend too much time writing the perfect algorithm. Or maybe you can't think of a better algorithm, and the one you have is too slow. Using tools like Numba can be one of the fastest and easiest to apply improvements!&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Find Item in a List</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Thu, 24 Sep 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/find-item-in-a-list-526g</link>
      <guid>https://forem.com/switowski/find-item-in-a-list-526g</guid>
      <description>&lt;h2&gt;
  
  
  Find a number
&lt;/h2&gt;

&lt;p&gt;If you want to find the first number that matches some criteria, what do you do? The easiest way is to write a loop that checks numbers one by one and returns when it finds the correct one.&lt;/p&gt;

&lt;p&gt;Let's say we want to get the first number divided by 42 and 43 (that's 1806). If we don't have a predefined set of elements (in this case, we want to check all the numbers starting from 1), we might use a "while loop".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# find_item.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;while_loop&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="c1"&gt;# You don't need to use parentheses, but they improve readability
&lt;/span&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's pretty straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start from number 1&lt;/li&gt;
&lt;li&gt;Check if that number can be divided by 42 and 43.

&lt;ul&gt;
&lt;li&gt;If yes, return it (this stops the loop)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Otherwise, check the next number&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Find a number in a list
&lt;/h2&gt;

&lt;p&gt;If we have a list of items that we want to check, we will use a "for loop" instead. I know that the number I'm looking for is smaller than 10 000, so let's use that as the upper limit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# find_item.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;for_loop&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's compare both solutions (benchmarks are done with &lt;strong&gt;Python 3.8&lt;/strong&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import while_loop"&lt;/span&gt; &lt;span class="s2"&gt;"while_loop()"&lt;/span&gt;
2000 loops, best of 5: 134 usec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import for_loop"&lt;/span&gt; &lt;span class="s2"&gt;"for_loop()"&lt;/span&gt;
2000 loops, best of 5: 103 usec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"While loop" is around 30% slower than the "for loop" (134/103≈1.301).&lt;/p&gt;

&lt;p&gt;Loops are optimized to iterate over a collection of elements. Trying to &lt;em&gt;manually&lt;/em&gt; do the iteration (for example, by referencing elements in a list through an index variable) will be a slower and often over-engineered solution.&lt;/p&gt;




&lt;h3&gt;
  
  
  Python 2 flashback
&lt;/h3&gt;

&lt;p&gt;In Python 2, functions like &lt;code&gt;range&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, or &lt;code&gt;zip&lt;/code&gt; were &lt;strong&gt;eager&lt;/strong&gt; , so they would always create the whole collection when initialized. All those elements would be loaded to the memory, increasing the execution time of your code and its memory usage. To avoid this behavior, you had to use their lazy equivalents like &lt;code&gt;xrange&lt;/code&gt;, &lt;code&gt;ifilter&lt;/code&gt;, or &lt;code&gt;izip&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Out of curiosity, let's see how slow is the &lt;code&gt;for_loop()&lt;/code&gt; function if we run it with Python 2.7.18 (the latest and last version of Python 2):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pyenv shell 2.7.18
&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import for_loop"&lt;/span&gt; &lt;span class="s2"&gt;"for_loop()"&lt;/span&gt;
10000 loops, best of 3: 151 usec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's almost 50% slower than running the same function in Python 3 (151/103≈1.4660). Updating Python version is &lt;strong&gt;one of the easiest performance wins&lt;/strong&gt; you can get!  &lt;/p&gt;

&lt;p&gt;If you are wondering what's pyenv and how to use it to quickly switch Python versions, check out &lt;a href="https://youtu.be/WkUBx3g2QfQ?t=2531"&gt;this section of my PyCon 2020 workshop&lt;/a&gt; on Python tools.  &lt;/p&gt;




&lt;p&gt;Let's go back to our "while loop" vs. "for loop" comparison. Does it matter if the element we are looking for is at the beginning or at the end of the list?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;while_loop2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;98&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;for_loop2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;98&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, we are looking for number 9702, which is at the very end of our list. Let's measure the performance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import while_loop2"&lt;/span&gt; &lt;span class="s2"&gt;"while_loop2()"&lt;/span&gt;
500 loops, best of 5: 710 usec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import for_loop2"&lt;/span&gt; &lt;span class="s2"&gt;"for_loop2()"&lt;/span&gt;
500 loops, best of 5: 578 usec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is almost no difference. "While loop" is around 22% slower this time (710/578≈1.223). I performed a few more tests (up to a number close to 100 000 000), and the difference was always similar (in the range of 20-30% slower).&lt;/p&gt;

&lt;h2&gt;
  
  
  Find a number in an infinite list
&lt;/h2&gt;

&lt;p&gt;So far, the collection of items we wanted to iterate over was limited to the first 10 000 numbers. But what if we don't know the upper limit? In this case, we can use the &lt;a href="https://docs.python.org/3/library/itertools.html#itertools.count"&gt;count&lt;/a&gt; function from the &lt;code&gt;itertools&lt;/code&gt; library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_numbers&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;count(start=0, step=1)&lt;/code&gt; will start counting numbers from the &lt;code&gt;start&lt;/code&gt; parameter, adding the &lt;code&gt;step&lt;/code&gt; in each iteration. In my case, I need to change the start parameter to 1, so it works the same as the previous examples.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;count&lt;/code&gt; works almost the same as the "while loop" that we made at the beginning. How about the speed?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import count_numbers"&lt;/span&gt; &lt;span class="s2"&gt;"count_numbers()"&lt;/span&gt;
2000 loops, best of 5: 109 usec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's almost the same as the "for loop" version. So &lt;code&gt;count&lt;/code&gt; is a good replacement if you need an &lt;strong&gt;infinite counter&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about a list comprehension?
&lt;/h2&gt;

&lt;p&gt;A typical solution for iterating over a list of items is to use a list comprehension. But we want to exit the iteration as soon as we find our number, and that's not easy to do with a list comprehension. It's a great tool to go over the whole collection, but not in this case.&lt;/p&gt;

&lt;p&gt;Let's see how bad it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_comprehension&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import list_comprehension"&lt;/span&gt; &lt;span class="s2"&gt;"list_comprehension()"&lt;/span&gt;
500 loops, best of 5: 625 usec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's really bad - it's a few times slower than other solutions! It takes the same amount of time, no matter if we search for the first or last element. And we can't use &lt;code&gt;count&lt;/code&gt; here.&lt;/p&gt;

&lt;p&gt;But using a list comprehension points us in the right direction - we need something that returns the first element it finds and then stops iterating. And that thing is a &lt;strong&gt;generator&lt;/strong&gt;! We can use a generator expression to grab the first element matching our criteria.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find item with a generator expression
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whole code looks very similar to a list comprehension, but we can actually use &lt;code&gt;count&lt;/code&gt;. Generator expression will execute only enough code to return the next element. Each time you call &lt;code&gt;next()&lt;/code&gt;, it will resume work in the same place where it stopped the last time, grab the next item, return it, and stop again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import generator"&lt;/span&gt; &lt;span class="s2"&gt;"generator()"&lt;/span&gt;
2000 loops, best of 5: 110 usec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes almost the same amount of time as the best solution we have found so far. And I find this syntax much easier to read - as long as we don't put too many &lt;code&gt;if&lt;/code&gt;s there!&lt;/p&gt;

&lt;p&gt;Generators have the additional benefit of being able to "suspend" and "resume" counting. We can call &lt;code&gt;next()&lt;/code&gt; multiple times, and each time we get the next element matching our criteria. If we want to get the first three numbers that can be divided by 42 and 43 - here is how easily we can do this with a generator expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generator_3_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare it with the "for loop" version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;for_loop_3_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's benchmark both versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import for_loop_3_items"&lt;/span&gt; &lt;span class="s2"&gt;"for_loop_3_items()"&lt;/span&gt;
1000 loops, best of 5: 342 usec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from find_item import generator_3_items"&lt;/span&gt; &lt;span class="s2"&gt;"generator_3_items()"&lt;/span&gt;
1000 loops, best of 5: 349 usec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Performance-wise, both functions are almost identical. So when would you use one over the other? "For loop" lets you write more complex code. You can't put nested "if" statements or multiline code with side effects inside a generator expression. But if you only do simple filtering, generators can be much easier to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Generator expression combined with &lt;code&gt;next()&lt;/code&gt; is a great way to grab one or more elements based on specific criteria. It's memory-efficient, fast, and easy to read - as long as you keep it simple. When the number of "if statements" in the generator expression grows, it becomes much harder to read (and write).&lt;/p&gt;

&lt;p&gt;With complex filtering criteria or many &lt;code&gt;if&lt;/code&gt;s, "for loop" is a more suitable choice that doesn't sacrifice the performance.&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Python: Ask for Forgiveness or Look Before You Leap?</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Thu, 20 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/python-ask-for-forgiveness-or-look-before-you-leap-1ak6</link>
      <guid>https://forem.com/switowski/python-ask-for-forgiveness-or-look-before-you-leap-1ak6</guid>
      <description>&lt;p&gt;“Ask for forgiveness” and “look before you leap” (sometimes also called “ask for permission”) are two opposite approaches to writing code. If you “look before you leap”, you first check if everything is set correctly, then you perform an action. For example, you want to read text from a file. What could go wrong with that? Well, the file might not be in the location where you expect it to be. So, you first check if the file exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path/to/file.txt"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Or from Python 3.4
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/path/to/file"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if the file exists, maybe you don't have permission to open it? So let's check if you can read it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path/to/file.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R_OK&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if the file is corrupted? Or if you don't have enough memory to read it? This list could go on. Finally, when you think that you checked every possible corner-case, you can open and read it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path/to/file.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on what you want to do, there might be quite a lot of checks to perform. And even when you think you covered everything, there is no guarantee that some unexpected problems won't prevent you from reading this file. So, instead of doing all the checks, you can "ask for forgiveness."&lt;/p&gt;

&lt;p&gt;With "ask for forgiveness," you don't check anything. You perform whatever action you want, but you wrap it in a &lt;code&gt;try/catch&lt;/code&gt; block. If an exception happens, you handle it. You don't have to think about all the things that can go wrong, your code is much simpler (no more nested ifs), and you will usually catch more errors that way. That's why the Python community, in general, prefers this approach, often called &lt;a href="https://docs.python.org/3/glossary.html#term-eafp"&gt;"EAFP"&lt;/a&gt; - "Easier to ask for forgiveness than permission."&lt;/p&gt;

&lt;p&gt;Here is a simple example of reading a file with the "ask for forgiveness" approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path/to/file.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Handle the error or just ignore it
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are catching the &lt;code&gt;IOError&lt;/code&gt;. If you are not sure what kind of exception can be raised, you could catch all of them with the &lt;code&gt;BaseException&lt;/code&gt; class, but in general, it's a bad practice. It will catch every possible exception (including, for example, &lt;code&gt;KeyboardInterrupt&lt;/code&gt; when you want to stop the process), so try to be more specific.&lt;/p&gt;

&lt;p&gt;"Ask for forgiveness" is cleaner. But which one is faster?&lt;/p&gt;

&lt;h2&gt;
  
  
  "Ask For Forgiveness" vs "Look Before You Leap" - speed
&lt;/h2&gt;

&lt;p&gt;Time for a simple test. Let's say that I have a class, and I want to read an attribute from this class. But I'm using inheritance, so I'm not sure if the attribute is defined or not. I need to protect myself, by either checking if it exists ("look before you leap") or catching the &lt;code&gt;AttributeError&lt;/code&gt; ("ask for forgiveness"):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# permission_vs_forgiveness.py
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"world"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseClass&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="n"&gt;FOO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Look before you leap
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_lbyl&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;

&lt;span class="c1"&gt;# Ask for forgiveness
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_aff&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's measure the speed of both functions.&lt;/p&gt;

&lt;p&gt;For benchmarking, I'm using the standard &lt;a href="https://docs.python.org/3/library/timeit.html"&gt;timeit&lt;/a&gt; module and &lt;strong&gt;Python 3.8&lt;/strong&gt;. I describe my setup and some assumptions in the &lt;a href="https://switowski.com/blog/writing-faster-python-intro"&gt;Introduction to the Writing Faster Python&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from permission_vs_forgiveness import test_lbyl"&lt;/span&gt; &lt;span class="s2"&gt;"test_lbyl()"&lt;/span&gt;
2000000 loops, best of 5: 155 nsec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from permission_vs_forgiveness import test_aff"&lt;/span&gt; &lt;span class="s2"&gt;"test_aff()"&lt;/span&gt;
2000000 loops, best of 5: 118 nsec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Look before you leap" is around &lt;strong&gt;30% slower&lt;/strong&gt; (155/118≈1.314).&lt;/p&gt;

&lt;p&gt;What happens if we increase the number of checks? Let's say that this time we want to check for three attributes, not just one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# permission_vs_forgiveness.py
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"world"&lt;/span&gt;
    &lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"world"&lt;/span&gt;
    &lt;span class="n"&gt;baz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"world"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseClass&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="n"&gt;FOO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Look before you leap
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_lbyl2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"baz"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;baz&lt;/span&gt;

&lt;span class="c1"&gt;# Ask for forgiveness
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_aff2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;baz&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from permission_vs_forgiveness import test_lbyl2"&lt;/span&gt; &lt;span class="s2"&gt;"test_lbyl2()"&lt;/span&gt;
500000 loops, best of 5: 326 nsec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from permission_vs_forgiveness import test_aff2"&lt;/span&gt; &lt;span class="s2"&gt;"test_aff2()"&lt;/span&gt;
2000000 loops, best of 5: 176 nsec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Look before you leap" is now around &lt;strong&gt;85% slower&lt;/strong&gt; (326/176≈1.852). So the "ask for forgiveness" is not only much easier to read and robust but, in many cases, also faster. Yes, you read it right, "in &lt;strong&gt;many&lt;/strong&gt; cases," not "in &lt;strong&gt;every&lt;/strong&gt; case!"&lt;/p&gt;

&lt;h2&gt;
  
  
  The main difference between "EAFP" and "LBYL"
&lt;/h2&gt;

&lt;p&gt;What happens if the attribute is actually not defined? Take a look at this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# permission_vs_forgiveness.py
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;  &lt;span class="c1"&gt;# "hello" attribute is now removed
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseClass&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="n"&gt;FOO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Look before you leap
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_lbyl3&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;

&lt;span class="c1"&gt;# Ask for forgiveness
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_aff3&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;FOO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from permission_vs_forgiveness import test_lbyl3"&lt;/span&gt; &lt;span class="s2"&gt;"test_lbyl3()"&lt;/span&gt;
2000000 loops, best of 5: 135 nsec per loop

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"from permission_vs_forgiveness import test_aff3"&lt;/span&gt; &lt;span class="s2"&gt;"test_aff3()"&lt;/span&gt;
500000 loops, best of 5: 562 nsec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tables have turned. "Ask for forgiveness" is now over &lt;strong&gt;four times&lt;/strong&gt; as slow as "Look before you leap" (562/135≈4.163). That's because this time, our code throws an exception. And &lt;strong&gt;handling exceptions is expensive&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you expect your code to fail often, then "Look before you leap" might be much faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;"Ask for forgiveness" results in much cleaner code, makes it easier to catch errors, and in most cases, it's much faster. No wonder that &lt;a href="https://docs.python.org/3/glossary.html#term-eafp"&gt;EAFP&lt;/a&gt;(&lt;em&gt;"Easier to ask for forgiveness than permission"&lt;/em&gt;) is such a ubiquitous pattern in Python. Even in the example from the beginning of this article (checking if a file exists with &lt;code&gt;os.path.exists&lt;/code&gt;) - if you look at the source code of the &lt;code&gt;exists&lt;/code&gt; method, you will see that it's simply using a &lt;code&gt;try/except&lt;/code&gt;. "Look before you leap" often results in a longer code that is less readable (with nested &lt;code&gt;if&lt;/code&gt; statements) and slower. And following this pattern, you will probably sometimes miss a corner-case or two.&lt;/p&gt;

&lt;p&gt;As a rule of thumb - ask yourself: &lt;em&gt;"Is it more common that this code will throw an exception or not?"&lt;/em&gt; Use "look before you leap" if you expect some problems (and if you can predict what those problems will be). Otherwise, use the "ask for forgiveness" for cleaner and faster code.&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Let’s Write an IPython Extension the Hard Way</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Thu, 16 Jul 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/let-s-write-an-ipython-extension-the-hard-way-5aba</link>
      <guid>https://forem.com/switowski/let-s-write-an-ipython-extension-the-hard-way-5aba</guid>
      <description>&lt;p&gt;I love IPython. I love using it, I love writing about it, I love taking pictures with its core contributors (Hi Paul!). If I ever get invited to the &lt;a href="https://talkpython.fm/"&gt;“Talk Python To Me”&lt;/a&gt; podcast (not that I have anything interesting to talk about), and Michael Kennedy is going to ask me what my favorite Python package is, you know what I’m going to say? Yep, IPython.&lt;/p&gt;

&lt;p&gt;So, when a friend of mine asked me what kind of lightning talk I want to prepare for one of the upcoming micro-conferences, my first thought was: &lt;em&gt;“Let’s try to make something cool with IPython”&lt;/em&gt;. Maybe I can improve the %rerun magic function?&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s wrong with the &lt;code&gt;%rerun&lt;/code&gt; function?
&lt;/h2&gt;

&lt;p&gt;You can use &lt;code&gt;%rerun&lt;/code&gt; to run commands from this or previous sessions of IPython. The most common use case for me is to get my IPython session to the same state when I left it last time. It usually happens when I close it because I think I’m done, and then I realize that I actually want to check one more thing &lt;strong&gt;with all the previous data that I had&lt;/strong&gt;. Well, in standard Python REPL, to rerun all the previous commands, I would have to press “arrow up” + “enter” a lot. With IPython, I can just run this one command: &lt;code&gt;%rerun ~1/&lt;/code&gt;, and it will execute all the commands from the previous session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="c1"&gt;# Close IPython and open a new session
&lt;/span&gt;
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rerun&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;Executing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But, there is one problem. If one of the past commands throws an exception, IPython will stop. It executes all the commands up to the exception, fails on the exception, and that’s it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;-----------------&lt;/span&gt;
&lt;span class="nb"&gt;ZeroDivisionError&lt;/span&gt;
&lt;span class="o"&gt;----&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="nb"&gt;ZeroDivisionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;division&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;zero&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="c1"&gt;# Close IPython and open a new session
&lt;/span&gt;
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rerun&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;Executing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt;
&lt;span class="o"&gt;-----------------&lt;/span&gt;
&lt;span class="nb"&gt;ZeroDivisionError&lt;/span&gt;
      &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="o"&gt;----&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="nb"&gt;ZeroDivisionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;division&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;zero&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="o"&gt;--------------&lt;/span&gt;
&lt;span class="nb"&gt;NameError&lt;/span&gt;
&lt;span class="o"&gt;----&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;

&lt;span class="nb"&gt;NameError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="s"&gt;'c'&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;defined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So if I want to execute the code after the exception, I have to figure out which command fails and rerun all commands after that line. For example, I can call: &lt;code&gt;%rerun ~1/5-66&lt;/code&gt;, and this will run all commands from 5th until 66th command.&lt;/p&gt;

&lt;p&gt;This has some limitations. First of all, you need to say which commands you want to run explicitly. And for that, you need to know the total number of commands for a given session. It’s no longer as convenient as just writing &lt;code&gt;%rerun ~1/&lt;/code&gt; and letting IPython figure out the rest.&lt;/p&gt;

&lt;p&gt;Second of all - I don’t know about you, but I have plenty of exceptions in my IPython sessions. After all, that’s why IPython is for - experimenting with the code to find out what works and then copying that. So for each of the failed commands, I would have to exclude this line from the &lt;code&gt;%rerun&lt;/code&gt;. This can quickly turn into a monstrosity like this: &lt;code&gt;%rerun ~1/1-2 ~1/4-6 ~1/8-12 ~1/15-23 ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It would be much better to temporarily ignore all the exceptions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Let’s write an extension that does that!”&lt;/em&gt; - I thought. &lt;em&gt;“It probably won’t take longer than 5 minutes to explain everything, so it’s a perfect material for a lightning talk”.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It took longer than 5 minutes. Much longer. But it was an exciting journey!&lt;/p&gt;

&lt;h2&gt;
  
  
  Is there a plugin for that?
&lt;/h2&gt;

&lt;p&gt;The first step was to check if someone already asked about this feature in the past. “Ignore exceptions” setting would be useful, so clearly someone must have thought about that before me.&lt;/p&gt;

&lt;p&gt;And someone did - already in 2012: &lt;a href="https://github.com/ipython/ipython/issues/1977"&gt;Add an ‘ignore exceptions’ mode to run commands in notebook&lt;/a&gt;. Starting from version 5.1, you can tag a cell in IPython notebooks with “raises-exception” to indicate that an exception is expected. IPython will ignore it and continue the execution. Unfortunately, it works only for the notebooks (so Jupyter Notebook), not for IPython REPL.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;“Fine, I will do it myself.”&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s look inside the &lt;code&gt;%rerun&lt;/code&gt; function and see how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# IPython/core/magics/history.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rerun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parameter_s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parameter_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'l:g:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;"l"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Last n lines
&lt;/span&gt;        &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;history_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="s"&gt;"g"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Search
&lt;/span&gt;        &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'g'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;
        &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;history_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;"rerun"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# The last match which isn't a %rerun
&lt;/span&gt;                &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;# No matches except %rerun
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Specify history ranges
&lt;/span&gt;        &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;history_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_range_by_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Last line
&lt;/span&gt;        &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;history_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No lines in history match specification"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="n"&gt;histlines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"=== Executing: ==="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;histlines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"=== Output: ==="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run_cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;store_history&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, it reuses the &lt;code&gt;%history&lt;/code&gt; magic command to grab a list of commands from history and executes them.&lt;/p&gt;

&lt;p&gt;I could monkey-patch this function and check if a given command is successful before adding it to the &lt;code&gt;hist&lt;/code&gt; list. But monkey-patching external libraries is a bad idea. I would have to fork IPython and take care of upgrading it, or I would be stuck on the current version. So I needed a better way.&lt;/p&gt;

&lt;p&gt;My first idea was to use one of &lt;a href="https://ipython.readthedocs.io/en/stable/config/callbacks.html"&gt;IPython’s events&lt;/a&gt;. You can register a callback function when a specific event happens, for example, before or after you run a cell. When this event happens, IPython calls your callback function. That looked like a perfect solution! I could check if a given cell throws an exception and, if it does, skip it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a callback for IPython events
&lt;/h2&gt;

&lt;p&gt;To register my custom callback, I needed to create an IPython extension (I wrote an article explaining &lt;a href="https://dev.to/switowski/ipython-extensions-guide-35cn"&gt;how extensions work and how to create one&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/.ipython/extensions/ignore_exceptions.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_ipython_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipython&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ipython&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'pre_run_cell'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clean_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;load_ipython_extension&lt;/code&gt; registers &lt;code&gt;clean_input&lt;/code&gt; callback that will be run before executing every cell. And &lt;code&gt;clean_input&lt;/code&gt; does the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes an argument (&lt;code&gt;info&lt;/code&gt;) containing various information about the cell we are about to run. This parameter is mandatory, and IPython will complain if we forget it.&lt;/li&gt;
&lt;li&gt;Extracts the content of the cell (&lt;code&gt;raw_cell&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Tries to run it.&lt;/li&gt;
&lt;li&gt;And if it raises an exception, replaces that cell with an empty string.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We save that file in the &lt;code&gt;~/.ipython/extensions/ignore_exceptions.py&lt;/code&gt; directory and enable our extension with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%load_ext ignore_exceptions

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we try it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In [1]: %load_ext ignore_exceptions

In [2]: 1/0
ZeroDivisionError
----&amp;gt; 1 1/0

ZeroDivisionError: division by zero

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…it doesn’t really work. There are multiple problems with this code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First of all - we are using &lt;code&gt;exec&lt;/code&gt;, which is not the best practice. But since the code we want to run is the same code we wrote before, I’m fine with that.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;info&lt;/code&gt; parameter can contain multiple commands. If we call &lt;code&gt;%rerun 1-10&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt; will contain the first ten commands from the current session. If the exception happens, let’s say in the 5th command, our current implementation will ignore all the commands. But we want to ignore only that 5th one and still execute the other nine commands. So that’s not going to work.&lt;/li&gt;
&lt;li&gt;And even if we didn’t have all those problems, &lt;strong&gt;overwriting the &lt;code&gt;info.raw_cell&lt;/code&gt; doesn’t actually change the code that will be executed&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We need something better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Input transformation
&lt;/h2&gt;

&lt;p&gt;If we want to modify the input cells before they get executed, there is a feature exactly for that called &lt;a href="https://ipython.readthedocs.io/en/stable/config/inputtransforms.html"&gt;“input transformation”&lt;/a&gt;. You write a function that takes the input, modifies it, and returns it. And you can register your input transformation as an extension, exactly as we did before.&lt;/p&gt;

&lt;p&gt;There are two types of input transformers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;input_transformers_cleanup&lt;/code&gt; - they run first. At this point, the cell is not guaranteed to contain valid Python code (e.g., IPython-specific syntax, like the magic functions).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;input_transformers_post&lt;/code&gt; - they run last, and here the cell contains only the Python code. In most cases, you want to use this one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Input transformation solves all our problems (except for using the &lt;code&gt;exec&lt;/code&gt;, but I will stick with it for simplicity). First of all, it’s an &lt;em&gt;“input”&lt;/em&gt; transformation, so we can use it to change the input cell’s content. And since each line of the input is a separate element in a list, we can just execute them one by one and discard those that raise an exception. So this is what we are going to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def clean_input(lines):
    new_lines = []
    for line in lines:
        try:
            exec(line)
            new_lines.append(line)
        except Exception:
            pass
    return new_lines

def load_ipython_extension(ipython):
    ipython.input_transformers_post.append(clean_input)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s load it and run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In [1]: %load_ext ignore_exceptions

In [2]: 1/0

In [3]: 1+2
Out[3]: 3
In [4]: my_number = 10

In [5]: my_number + 5
# Nothing is returned!

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s working, but partially. If we assign a variable (&lt;code&gt;In [4]&lt;/code&gt;) and then reference it (&lt;code&gt;In [5]&lt;/code&gt;), you will notice that nothing is returned! That’s because &lt;code&gt;my_number&lt;/code&gt; variable was saved in the &lt;strong&gt;local namespace&lt;/strong&gt; of IPython, and the &lt;code&gt;exec&lt;/code&gt; function call is running without access to those variables. Our extension tries to reference variable &lt;code&gt;my_number&lt;/code&gt;, gets a &lt;code&gt;NameError&lt;/code&gt;, and since our code is supposed to ignore errors, it ignores that line. Thus - nothing is printed!&lt;/p&gt;

&lt;p&gt;We need to tell &lt;code&gt;exec&lt;/code&gt; about all the variables that have been defined so far. If you check the &lt;a href="https://docs.python.org/3.8/library/functions.html#exec"&gt;signature of the exec() function&lt;/a&gt;, you will see that apart from the code to execute, we can also pass global and local variables. Bingo! Let’s grab the &lt;code&gt;user_ns&lt;/code&gt; from the &lt;code&gt;ipython&lt;/code&gt; object and pass it to the &lt;code&gt;exec&lt;/code&gt; function. &lt;code&gt;user_ns&lt;/code&gt; contains all the variables that were defined so far, but also all the imported modules, classes, etc.&lt;/p&gt;

&lt;p&gt;We need to change this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exec(line)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;into this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exec(line, None, ipython.user_ns)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the &lt;code&gt;clean_input&lt;/code&gt; function doesn’t have access to the &lt;code&gt;ipython&lt;/code&gt; object! And we can’t pass it as a parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def clean_input(lines, ipython):

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because input transformer functions need to have a specific signature and they can accept only one parameter - the list of lines. We could solve this problem using a class, but I prefer to use a technique called &lt;a href="https://en.wikipedia.org/wiki/Currying"&gt;currying&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def curry_clean(ipython):
    def clean_input(lines):
        new_lines = []
        for line in lines:
            try:
                exec(line, None, ipython.user_ns)
                new_lines.append(line)
            except Exception:
                pass
        return new_lines
    return clean_input

def load_ipython_extension(ipython):
    ipython.input_transformers_post.append(curry_clean(ipython))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s try again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In [1]: %load_ext ignore_exceptions

In [2]: 1/0

In [4]: my_number = 10

In [5]: my_number + 5
Out[5]: 10

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s working! Kind of…. We can declare a variable and reference it later, so that’s good. If the code throws an exception, it will be suppressed - that’s also what we want. But if we call a &lt;code&gt;print()&lt;/code&gt; function, we get the output three times!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In [6]: print('hello world')
hello world
hello world
hello world

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s because our input transformation function is actually called twice! Once in the &lt;code&gt;should_run_async()&lt;/code&gt; function and then in the &lt;code&gt;run_cell()&lt;/code&gt; function. That’s the behavior of version 7.15 of IPython - the latest one when I write this. And there is nothing you can do about it. If you ever decide to write some input transformations, keep in mind that &lt;strong&gt;they can run multiple times&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And when we actually execute the code, that’s the third &lt;code&gt;print()&lt;/code&gt;. This “triple printing” can be mildly annoying, but maybe it’s not that bad? All we want to do is to rerun previous commands in one go, so we can live with duplicated output that happens only once.&lt;/p&gt;

&lt;p&gt;But if printing happens multiple times, then every other command happens multiple times. And if we run a command that is not idempotent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In [7]: my_number = 10

In [8]: my_number += 1

In [9]: my_number
Out[9]: 13

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get a bug. 10 incremented by 1 should give us 11, but instead, we get 13. Modifications that we do in &lt;code&gt;try/except&lt;/code&gt; block get propagated back to our IPython session because we are accessing variables from that namespace. Remember the &lt;code&gt;ipython.user_ns&lt;/code&gt;? It works both ways - for accessing and &lt;strong&gt;assigning&lt;/strong&gt; variables.&lt;/p&gt;

&lt;p&gt;If we don’t want to modify the original namespace, let’s make a copy and execute our code there! Except that we would need to do a &lt;a href="https://docs.python.org/3.8/library/copy.html?#copy.deepcopy"&gt;deep copy&lt;/a&gt;. Why a deep copy? Because if we have mutable variables (like a list) and do a shallow copy, modifying the mutable variable in the copied namespace will still affect the original namespace. We need to cut the connections between both namespaces completely, so we use the deep copy.&lt;/p&gt;

&lt;p&gt;Deep copy basically pickles and un-pickles every object. Do you know what can’t be pickled? Modules. And do you know what IPython’s (or Python’s) namespace contains, except for functions and variables? Modules! So deep copy is going to crash.&lt;/p&gt;

&lt;p&gt;Unless we filter the namespace and only pickle things that can be pickled! In the end, I only need to copy variables. I won’t be able to modify modules anyway, so it’s fine to leave them in the namespace as they are. Let’s add a new function for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from copy import deepcopy

def copy_namespace(local_ns):
    copy_ns = {}
    for key, value in local_ns.items():
        try:
            copy_ns[key] = deepcopy(value)
        except TypeError:
            # TypeError is raised when an object can't be pickled
            copy_ns[key] = value
    return copy_ns

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here are two changes that we need to make in the &lt;code&gt;curry_clean&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def curry_clean(ipython):
    def clean_input(lines):
        new_lines = []
+ local_ns = copy_namespace(ipython.user_ns)
        for line in lines:
            try:
- exec(line, None, ipython.user_ns)
+ exec(line, None, local_ns)
                new_lines.append(line)
            except Exception:
                pass
        return new_lines
    return clean_input

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are almost there, but there are two problems left:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We still get a lot of duplicated output for &lt;code&gt;print&lt;/code&gt; and similar commands.&lt;/li&gt;
&lt;li&gt;Our function doesn’t work for multiline strings (so all &lt;code&gt;for-loops&lt;/code&gt;, function definitions, and similar multiline statements will crash it).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s solve them now. First, the duplicated output. We can suppress it by redirecting the standard output to &lt;code&gt;/dev/null&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import sys

sys.stdout = open(os.devnull, 'w')
print("I'm invisible")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run the above code, the output of a print statement won’t be displayed. Actually, no other output will be displayed afterward, so we need to set the &lt;code&gt;stdout&lt;/code&gt; back to the original value after the &lt;code&gt;try/except&lt;/code&gt; check.&lt;/p&gt;

&lt;p&gt;If we want to execute some code, run a function, and then execute more code, the best tool to use is a context manager. Let’s write one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import sys
from contextlib import contextmanager

@contextmanager
def no_output():
    original_stdout = sys.stdout
    sys.stdout = open(os.devnull, 'w')
    yield
    sys.stdout = original_stdout

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’m using the &lt;code&gt;contextmanager&lt;/code&gt; decorator to turn my function into a context manager (it saves me from writing boilerplate code). Here is what my function does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store the original stdout in a variable.&lt;/li&gt;
&lt;li&gt;Replace the &lt;code&gt;stdout&lt;/code&gt; with &lt;code&gt;/dev/null&lt;/code&gt; (whatever you write to &lt;code&gt;dev/null&lt;/code&gt; is gone).&lt;/li&gt;
&lt;li&gt;Yield - so run the code inside the context manager.&lt;/li&gt;
&lt;li&gt;And, finally, restore stdout to the initial value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, we wrap our &lt;code&gt;exec&lt;/code&gt; function with this context manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for line in lines:
+ with no_output():
        try:
            exec(line, None, local_ns)
            new_lines.append(line)
        except Exception:
            pass
return new_lines

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uff, almost there! The last part is to enable running multiline code. For that, we just need to check if the line starts with a whitespace character. If it does, combine it with the previous line.&lt;/p&gt;

&lt;p&gt;Let’s write a helper to transform multiline strings into one-line strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def combine_multiline(lines):
    new_lines = []
    for line in lines:
        if line.startswith(' ') and new_lines:
            new_lines[-1] += line
        else:
            new_lines.append(line)
    return new_lines

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second part of the &lt;code&gt;if line.startswith('') and new_lines&lt;/code&gt; is just a sanity check to make sure that &lt;code&gt;new_lines&lt;/code&gt; already contains items. Normally, we wouldn’t bother with checking for this. If the first line starts with spaces, there is something wrong with it, and it will fail during the execution anyway.&lt;/p&gt;

&lt;p&gt;Let’s call our function in the correct place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def clean_input(lines):
    new_lines = []
+ lines = combine_multiline(lines)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, our hackish input transformation &lt;em&gt;seems to&lt;/em&gt; be working!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Seems to&lt;/em&gt;, because there is an interesting bug. If we try to rerun a command that reloads our extension (&lt;code&gt;%reload ignore_exceptions&lt;/code&gt;), IPython will get into an infinite loop. It will try to add &lt;code&gt;clean_input&lt;/code&gt; callback again. This will run the callback itself. The callback will try to execute &lt;code&gt;%reload ignore_exceptions&lt;/code&gt; that will register &lt;code&gt;clean_input&lt;/code&gt; that will call the callback, and so on. Until it crashes.&lt;/p&gt;

&lt;p&gt;We can solve this problem by adding the &lt;strong&gt;“unload”&lt;/strong&gt; function that lets you disable an extension. In our case, we want to remove &lt;code&gt;clean_input&lt;/code&gt; from the list of &lt;code&gt;input_transformers_post&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def unload_ipython_extension(ipython):
    ipython.input_transformers_post = [
        f for f in ipython.input_transformers_post if f. __name__!= 'clean_input'
    ]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code goes through the list of all input transformers and removes the one called “clean_input”. We can’t replace the &lt;code&gt;if f. __name__!= 'clean_input'&lt;/code&gt; with &lt;code&gt;if f == curry_clean(ipython)&lt;/code&gt; because each time we call &lt;code&gt;curry_clean(ipython)&lt;/code&gt; it creates a different object. So the &lt;code&gt;curry_clean&lt;/code&gt; function added to the input transformers list has a different id than &lt;code&gt;curry_clean&lt;/code&gt; called in the comparison. We could use a singleton design pattern if we want, but we might just compare functions’ names, which will work just fine.&lt;/p&gt;

&lt;p&gt;With unload in place, our extension is finally ready. You can activate it, run some code, and notice that no exceptions are raised. You can also deactivate it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%unload_ext ignore_exceptions

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Meet %rerunplus: %rerun that ignores exceptions
&lt;/h2&gt;

&lt;p&gt;But how can we use it in combination with &lt;code&gt;%rerun&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;The simplest way is to create a new magic function that wraps a call to &lt;code&gt;%rerun&lt;/code&gt; in load/unload functions. Let’s make a new file inside &lt;code&gt;~/.ipython/profile_default/startup/&lt;/code&gt; directory and define our magic function there. During startup, IPython automatically executes files defined in this directory. It’s a good place to put magic functions that we want to enable in each IPython session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from IPython.core.magic import register_line_magic

@register_line_magic
def rerunplus(line):
    get_ipython().run_line_magic('load_ext', 'ignore_exceptions')
    get_ipython().run_line_magic('rerun', line)
    get_ipython().run_line_magic('unload_ext', 'ignore_exceptions')

def load_ipython_extension(ipython):
    ipython.register_magics(rerunplus)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your code editor will complain that the &lt;code&gt;get_ipython()&lt;/code&gt; is not defined, but actually, that’s a global function in IPython, so it’s fine. If you restart IPython, you will now have access to a &lt;code&gt;%rerunplus&lt;/code&gt; magic function. Let’s give it a try.&lt;/p&gt;

&lt;p&gt;First, run some code with exceptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = 1
b = 2
1/0
c = 3
myfile = open('beep')
d = 4

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, close and reopen IPython. If you try to rerun all the commands using the standard &lt;code&gt;%rerun&lt;/code&gt;, it will fail at &lt;code&gt;1/0&lt;/code&gt; exception. &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; variables will be set, but &lt;code&gt;c&lt;/code&gt; and &lt;code&gt;d&lt;/code&gt; won’t. But if we use our new, cool &lt;code&gt;%rerunplus&lt;/code&gt;, it will ignore all the exceptions and set all variables correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In [1]: %rerunplus ~1/
=== Executing: ===
a = 1
b = 2
1/0
c = 3
myfile = open('beep')
d = 4
=== Output: ===

In [2]: a
Out[2]: 1

In [3]: b
Out[3]: 2

In [4]: c
Out[4]: 3

In [5]: d
Out[5]: 4

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://gist.github.com/switowski/301320fb6388822a76644461219e4386"&gt;Here is the complete code&lt;/a&gt; from this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;We have built an improved version of &lt;code&gt;%rerun&lt;/code&gt; that ignores exceptions. Is it efficient? Nope. It’s a hackish solution that you should use only with your own code, especially since we use &lt;code&gt;exec&lt;/code&gt;. &lt;code&gt;exec&lt;/code&gt; should never be used to run some code that you have no control over! But those 60 lines of code are good enough to fix one of the biggest shortcomings of the original &lt;code&gt;%rerun&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;P.S.&lt;/p&gt;

&lt;p&gt;What happens if we try to &lt;code&gt;%rerun&lt;/code&gt; code that contains &lt;code&gt;%rerun&lt;/code&gt; instruction? We get a &lt;code&gt;RecursionError&lt;/code&gt;. This is a bug that both standard &lt;code&gt;%rerun&lt;/code&gt; and my &lt;code&gt;%rerunplus&lt;/code&gt; share. So if you got curious about digging into IPython, you can take my extension and try to fix it on your own.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ipython</category>
    </item>
    <item>
      <title>My Favorite CLI Tools</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Fri, 19 Jun 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/my-favorite-cli-tools-4p4g</link>
      <guid>https://forem.com/switowski/my-favorite-cli-tools-4p4g</guid>
      <description>&lt;p&gt;Previously, I wrote about &lt;a href="https://dev.to/switowski/my-favorite-macbook-tools-2apf"&gt;my favorite Mac apps&lt;/a&gt;. But I spend half of my time in the terminal, and I have a handful of CLI tools that makes my life easier. Here are some of them:&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools that I use every day
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://fishshell.com/"&gt;fish shell&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4GHOmQOv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-fish.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4GHOmQOv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-fish.jpg" alt="fish shell website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shell - the most important tool that you use every time you open the terminal. I’ve used Bash and Z shell in the past, and currently, I’m using fish. It’s a great shell with plenty of features out of the box, like the autosuggestions, syntax highlighting, or switching between folders with ⌥+→ and ⌥+←.&lt;/p&gt;

&lt;p&gt;On the one hand, this makes it perfect for beginners, because you don’t have to set up anything. On the other hand, because it’s using a different syntax than other shells, you usually can’t just paste scripts from the internet. You either have to change the incompatible commands to &lt;a href="https://fishshell.com/docs/current/index.html#syntax-overview"&gt;fish scripts&lt;/a&gt; or start a Bash session to run the bash scripts. I understand the idea behind this change (Bash is not the easiest language to use), but it doesn’t benefit me in any way. I write bash/fish scripts too seldom to memorize the syntax, so I always have to relearn it from scratch. And there are fewer resources for fish scripts than for bash scripts. I usually end up reading the documentation, instead of copy-pasting ready-made scripts from StackOverflow.&lt;/p&gt;

&lt;p&gt;Do I recommend fish? Yes! Switching shells is easy, so give it a try. Especially if you don’t like to tinker with your shell and want to have something that works great with minimal configuration.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fish plugins
&lt;/h4&gt;

&lt;p&gt;You can add more features to fish with plugins. The easiest way to install them is to use a plugin manager like &lt;a href="https://github.com/jorgebucaran/fisher"&gt;Fisher&lt;/a&gt;, &lt;a href="https://github.com/oh-my-fish/oh-my-fish"&gt;Oh My Fish&lt;/a&gt;, or &lt;a href="https://github.com/danhper/fundle"&gt;fundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Right now, I’m using Fisher with just three plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/franciscolourenco/done"&gt;franciscolourenco/done&lt;/a&gt; - sends a notification when a long-running script is done. I don’t have a terminal open all the time. I’m using a &lt;a href="http://guake-project.org/"&gt;Guake style&lt;/a&gt; terminal that drops down from the top of the screen when I need it and hides when I don’t. With this plugin, when I run scripts that take longer than a few seconds, I get a macOS notification when they finish.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/evanlucas/fish-kubectl-completions"&gt;evanlucas/fish-kubectl-completions&lt;/a&gt; - provides autocompletion for kubectl (Kubernetes command line tool).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jethrokuan/fzf"&gt;fzf&lt;/a&gt; - integrates the fzf tool (see below) with fish.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had more plugins in the past (rbenv, pyenv, nodenv, fzf, z), but I switched to different tools to avoid slowing down my shell (a mistake that I did in the past with Z shell).&lt;/p&gt;

&lt;p&gt;If you want to see more resources for fish, check out the &lt;a href="https://github.com/jorgebucaran/awesome-fish"&gt;awesome-fish&lt;/a&gt; repository. Compared with Z shell and Bash, fish has fewer plugins, so it’s not the best option if you want to tweak it a lot. For me - that’s a plus. It stops me from enabling too many plugins and then complaining that it’s slow 😉.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://starship.rs/"&gt;Starship&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aYuqfFe2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-starship.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aYuqfFe2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-starship.jpg" alt="Starship website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If I had to choose one favorite tool from this whole list - it would be Starship. Starship is a prompt that works for any shell. You install it, add one line of config to your &lt;code&gt;.bashrc&lt;/code&gt;/&lt;code&gt;.zshrc&lt;/code&gt;/&lt;code&gt;config.fish&lt;/code&gt;, and it takes care of the rest.&lt;/p&gt;

&lt;p&gt;It shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;git status of the current directory and different symbols, depending on if you have new files, pending changes, stashes, etc.&lt;/li&gt;
&lt;li&gt;Python version if you are in a Python project folder (the same applies to Go/Node/Rust/Elm and many other programming languages)&lt;/li&gt;
&lt;li&gt;How long it took the previous command to execute (if it was longer than a few milliseconds)&lt;/li&gt;
&lt;li&gt;Error indicator if the last command failed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vlp1kXPM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-starship2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vlp1kXPM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-starship2.jpg" alt="Starship prompt in action"&gt;&lt;/a&gt;Starship prompt in action&lt;/p&gt;

&lt;p&gt;And a bazillion other information. But, in a smart way! If you are not in a git repository, it hides the git info. If you are not in a Python project - there is no Python version (because there is no point in displaying it). It never overwhelms you with too much information, and the prompt stays beautiful, useful, and minimalistic.&lt;/p&gt;

&lt;p&gt;Did I mention that it’s fast? It’s written in Rust, and even with so many features, it’s still faster than all my previous prompts! I’m very picky about my prompt, so I was usually hacking my own version. I was taking functions from existing prompts and gluing it together to make sure I only have things that I need and it stays fast. That’s why I was skeptical about Starship. &lt;em&gt;“There is no way that an external tool can be faster than my meticulously crafted prompt!&lt;/em&gt;” Well, I was wrong. Give it a try, and I’m sure you are going to love it! Huge kudos to the creators of Starship!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rupa/z"&gt;z&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mS_zE34Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mS_zE34Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-z.gif" alt="z tool in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;“z” lets you quickly jump around your filesystem. It memorizes the folders that you visit, and after a short learning time, you can move between them using &lt;code&gt;z path_of_the_folder_name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if I often go to folder &lt;code&gt;~/work/src/projects&lt;/code&gt;, I can just run &lt;code&gt;z pro&lt;/code&gt; and immediately jump there. z’s algorithm is based on &lt;strong&gt;frecency&lt;/strong&gt; - a combination of &lt;strong&gt;frequency&lt;/strong&gt; and &lt;strong&gt;recency&lt;/strong&gt; that works very well. If it memorizes a folder that you don’t want to use, you can always remove it manually.&lt;/p&gt;

&lt;p&gt;It speeds up moving between commonly visited folders on my computer and saves me a lot of keystrokes (and path memorization).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dCtFN2hI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-fzf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dCtFN2hI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-fzf.jpg" alt="fzf in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;fzf stands for &lt;em&gt;“fuzzy finder”&lt;/em&gt;. It’s a general-purpose tool that lets you find files, commands in the history, processes, git commits, and more using a &lt;strong&gt;fuzzy search&lt;/strong&gt;. You type some letters, and it tries to match those letters anywhere in the list of results. The more letters you type, the more accurate the results are. You probably know this type of search from your code editor - when you use the command to open a file, and you type just part of the file name instead of the full path - that’s a fuzzy search.&lt;/p&gt;

&lt;p&gt;I use it through the &lt;a href="https://github.com/jethrokuan/fzf"&gt;fish fzf plugin&lt;/a&gt;, so I can search through command history or quickly open a file. It’s another small tool that saves me time every day.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/sharkdp/fd"&gt;fd&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iE0aKwSR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-fd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iE0aKwSR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-fd.gif" alt="fd in action"&gt;&lt;/a&gt;find (left) versus fd (right)&lt;/p&gt;

&lt;p&gt;Like the &lt;code&gt;find&lt;/code&gt; command but much simpler to use, faster, and comes with good default settings.&lt;/p&gt;

&lt;p&gt;You want to find a file called “invoice,” but you are not sure what extension it has? Or maybe it was a directory that was holding all your invoices, not a single file? You can either roll up your sleeves and start writing those regex patterns for the &lt;code&gt;find&lt;/code&gt; command or just run &lt;code&gt;fd invoice&lt;/code&gt;. For me, the choice is easy 😉.&lt;/p&gt;

&lt;p&gt;By default, fd ignores files and directories that are hidden or listed in the &lt;code&gt;.gitignore&lt;/code&gt;. Most of the time - that’s what you want, but for those rare cases when I need to disable this feature, I have an alias: &lt;code&gt;fda='fd -IH'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The output is nicely colorized and, &lt;a href="https://github.com/sharkdp/fd#benchmark"&gt;according to the benchmarks&lt;/a&gt; (or the GIF above), it’s even faster than &lt;code&gt;find&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/BurntSushi/ripgrep"&gt;ripgrep&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ar8nth2_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-rg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ar8nth2_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-rg.gif" alt="ripgrep in action"&gt;&lt;/a&gt;Poor grep (on the left) was running for ages through all the node_modules before finding something useful&lt;/p&gt;

&lt;p&gt;In a similar manner to &lt;code&gt;fd&lt;/code&gt; mentioned above, &lt;code&gt;ripgrep&lt;/code&gt; is an alternative to the &lt;code&gt;grep&lt;/code&gt; command - much faster one, with sane defaults and colorized output.&lt;/p&gt;

&lt;p&gt;It skips files ignored by &lt;code&gt;.gitignore&lt;/code&gt; and hidden ones, so you will probably need this alias: &lt;code&gt;rga='rg -uuu'&lt;/code&gt;. It disables all smart filtering and makes &lt;code&gt;ripgrep&lt;/code&gt; behave as standard grep.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://hisham.hm/htop/"&gt;htop&lt;/a&gt; and &lt;a href="https://nicolargo.github.io/glances/"&gt;glances&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The most common tool to show information about processes running on Linux or Mac is called &lt;code&gt;top&lt;/code&gt;. It’s the best friend of every system administrator. And, even if you are mostly doing web development like me, it’s useful to see what’s going on with your computer. You know, just to see if it was Docker or Chrome that ate all your RAM this time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LTamxERV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-htop.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LTamxERV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-htop.jpg" alt="htop in action"&gt;&lt;/a&gt;htop is an excellent alternative for top&lt;/p&gt;

&lt;p&gt;&lt;code&gt;top&lt;/code&gt; is quite basic, so most people switch to &lt;a href="https://hisham.hm/htop/"&gt;htop&lt;/a&gt;. &lt;code&gt;htop&lt;/code&gt; is top on steroids - colorful, with plenty of options, and overall more comfortable to use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A3O_xzAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-glances.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A3O_xzAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-glances.jpg" alt="Glances at a glance ;)"&gt;&lt;/a&gt;glances gives you a quick overview of your system&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nicolargo.github.io/glances/"&gt;glances&lt;/a&gt; is a complementary tool to &lt;code&gt;htop&lt;/code&gt;. Apart from listing all the processes with their CPU and memory usage, it also displays additional information about your system.&lt;/p&gt;

&lt;p&gt;You can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;network or disks usage&lt;/li&gt;
&lt;li&gt;used and total space on your filesystem&lt;/li&gt;
&lt;li&gt;data from different sensors (like the battery)&lt;/li&gt;
&lt;li&gt;and a list of processes that recently consumed an excessive amount of resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I still use &lt;code&gt;htop&lt;/code&gt; for faster filtering and killing processes, but I use &lt;code&gt;glances&lt;/code&gt; to quickly &lt;em&gt;glance&lt;/em&gt; at what’s going on with my computer. It comes with API, Web UI, and various export formats, so you can take system monitoring to the next level. I highly recommend it!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://pypi.org/project/virtualenv/"&gt;virtualenv&lt;/a&gt; and &lt;a href="https://github.com/justinmayer/virtualfish"&gt;virtualfish&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W8MW-naL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-virtualenv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W8MW-naL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-virtualenv.jpg" alt="Virtualenv website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Virtualenv is a tool for creating virtual environments in Python (I like it more than the built-in &lt;code&gt;venv&lt;/code&gt; module).&lt;/p&gt;

&lt;p&gt;VirtualFish is virtual environment manager for the fish shell (if you are not using fish, check out &lt;a href="https://pypi.org/project/virtualenvwrapper"&gt;virtualenvwrapper&lt;/a&gt;). It provides a bunch of additional commands to create, list, or delete virtual environments quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/pyenv/pyenv"&gt;pyenv&lt;/a&gt;, &lt;a href="https://github.com/nodenv/nodenv"&gt;nodenv&lt;/a&gt;, and &lt;a href="https://github.com/rbenv/rbenv"&gt;rbenv&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sUSzvELi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-pyenv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sUSzvELi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-pyenv.jpg" alt="pyenv in action"&gt;&lt;/a&gt;pyenv makes it easy to switch Python versions&lt;/p&gt;

&lt;p&gt;Pyenv, nodenv, and rubyenv are tools for managing different versions of Python, Node, and Ruby on my computer.&lt;/p&gt;

&lt;p&gt;Let’s say you want to have two versions of Python on your computer. Maybe you are working on two different projects, or you still need to support Python 2. Managing different Python versions is hard. You need to make sure that different projects install packages with the correct version. If you are not careful, it’s easy to mess up this fragile setup and overwrite binaries used by other packages.&lt;/p&gt;

&lt;p&gt;Version manager helps a lot and turns this nightmare into a pretty manageable task. Good version manager can swap the Python version globally or “per folder”. And every version is isolated from others.&lt;/p&gt;

&lt;p&gt;I’ve recently found a tool called &lt;a href="https://github.com/asdf-vm/asdf"&gt;asdf&lt;/a&gt; that can replace pyenv, nodenv, rbenv, and other *envs with one tool to rule them all. It provides version management for pretty much &lt;a href="https://asdf-vm.com/#/plugins-all?id=plugin-list"&gt;any programming language&lt;/a&gt;, and I will definitely give it a try next time I need to set up a version manager for a programming language.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/pipxproject/pipx"&gt;pipx&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dwXfY7qi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-pipx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dwXfY7qi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-pipx.jpg" alt="pipx logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Virtualenv solves many problems with package management in Python, but there is one more use case to cover. If I want to install a Python package globally (because it’s a standalone tool, like &lt;code&gt;glances&lt;/code&gt; mentioned before), I have a problem. Installing packages outside of a virtual environment is a bad idea and can lead to problems in the future. On the other hand, if I decide to use a virtual environment, then I need to activate that virtual environment each time I want to run my tool. Not the most convenient solution either.&lt;/p&gt;

&lt;p&gt;It turns out that &lt;code&gt;pipx&lt;/code&gt; tool can solve this problem. It installs Python packages into separate environments (so there is no risk that their dependencies will clash). But, at the same time, CLI commands provided by those tools are available globally. So I don’t have to activate anything - &lt;code&gt;pipx&lt;/code&gt; will do this for me!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/bcicen/ctop"&gt;ctop&lt;/a&gt; and &lt;a href="https://github.com/jesseduffield/lazydocker"&gt;lazydocker&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qm5NooEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-ctop.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qm5NooEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-ctop.gif" alt="ctop in action"&gt;&lt;/a&gt;ctop in action (source: &lt;a href="https://github.com/bcicen/ctop"&gt;https://github.com/bcicen/ctop&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Both of those tools are useful when you are working with Docker. &lt;code&gt;ctop&lt;/code&gt; is a top-like interface for Docker containers. It gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A list of running and stopped containers&lt;/li&gt;
&lt;li&gt;Statistics like memory usage, CPU, and an additional detailed window for each container (with open ports and other information)&lt;/li&gt;
&lt;li&gt;A quick menu to stop, kill, or show logs of a given container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s so much nicer than trying to figure out all this information from &lt;code&gt;docker ps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nesp0aJR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-lazydocker.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nesp0aJR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-lazydocker.gif" alt="lazydocker in action"&gt;&lt;/a&gt;lazydocker is my favorite Docker tool (source: &lt;a href="https://github.com/jesseduffield/lazydocker"&gt;https://github.com/jesseduffield/lazydocker&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;And if you think that &lt;code&gt;ctop&lt;/code&gt; was cool, wait until you try &lt;code&gt;lazydocker&lt;/code&gt;! It’s a full-fledged terminal UI for managing Docker with even more features. My favorite tool when it comes to Docker!&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools that I don’t use every day
&lt;/h2&gt;

&lt;p&gt;Apart from the tools that I use almost every day, there are some that I collected over the years and found them particularly useful for specific tasks. There is something to record GIFs from the terminal (that you can pause and copy text from!), list directory structure, connect to databases, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XtoErNtg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-homebrew.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XtoErNtg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-homebrew.jpg" alt="Homebrew website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Homebrew needs no introduction if you are using a Mac. It’s a &lt;em&gt;de facto&lt;/em&gt; package manager for macOS. It even has a GUI version called &lt;a href="https://www.cakebrew.com/"&gt;Cakebrew&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://asciinema.org/"&gt;asciinema&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TyBVuzHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-asciinema.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TyBVuzHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-asciinema.jpg" alt="Asciinema website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;asciinema&lt;/code&gt; is a tool that you can use to record your terminal sessions. But, unlike recording GIFs, it will let your viewers select and copy the code from those recordings!&lt;/p&gt;

&lt;p&gt;It’s a great help for recording coding tutorials - not many things are as frustrating as typing long commands because the instructor didn’t provide you with code snippets.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.colordiff.org/"&gt;colordiff&lt;/a&gt; and &lt;a href="https://github.com/so-fancy/diff-so-fancy"&gt;diff-so-fancy&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DF7pKRRI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-colordiff.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DF7pKRRI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-colordiff.jpg" alt="Colordiff in action"&gt;&lt;/a&gt;colordiff brings some colors to your diffs&lt;/p&gt;

&lt;p&gt;I rarely do diffs (compare differences between two files) in the terminal anymore, but if you need to do one, use &lt;code&gt;colordiff&lt;/code&gt; instead of the unusable &lt;code&gt;diff&lt;/code&gt; command. &lt;code&gt;colordiff&lt;/code&gt; colorizes the output, so it’s much easier to see the changes instead of trying to follow all the “&amp;lt;” and “&amp;gt;” signs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pV-de-NA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-diff-so-fancy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pV-de-NA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-diff-so-fancy.jpg" alt="diff-so-fancy in action"&gt;&lt;/a&gt;diff-so-fancy - even better alternative to colordiff&lt;/p&gt;

&lt;p&gt;For running &lt;code&gt;git diff&lt;/code&gt; and &lt;code&gt;git show&lt;/code&gt; commands, there is an even better tool called &lt;a href="https://github.com/so-fancy/diff-so-fancy"&gt;diff-so-fancy&lt;/a&gt;. It further improves how the diff looks like by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;highlighting changed words (instead of the whole lines)&lt;/li&gt;
&lt;li&gt;simplifying the headers for changed files&lt;/li&gt;
&lt;li&gt;stripping the + and - symbols (you already have colors for this)&lt;/li&gt;
&lt;li&gt;clearly indicating new and deleted empty lines&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  tree (&lt;code&gt;brew install tree&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;If you want to present the content of a given directory, &lt;code&gt;tree&lt;/code&gt; is a go-to tool to do that. It displays all the subdirectories and files in a nice, tree-like structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── recovery.md
├── README.md
├── archive
├── automator
│   ├── Open&lt;span class="se"&gt;\ &lt;/span&gt;Iterm2.workflow
│   │   └── Contents
│   │   ├── Info.plist
│   │   ├── QuickLook
│   │   │   └── Thumbnail.png
│   │   └── document.wflow
│   └── Start&lt;span class="se"&gt;\ &lt;/span&gt;Screen&lt;span class="se"&gt;\ &lt;/span&gt;Saver.workflow
├── brew-cask.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://dev.to/scottw/bat-57pg-temp-slug-2229370"&gt;bat&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yDeF27wD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-bat.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yDeF27wD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-bat.jpg" alt="NANANANANANA, bat!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like &lt;code&gt;cat&lt;/code&gt; (command most commonly used to display the content of a file in a terminal) but better.&lt;/p&gt;

&lt;p&gt;Adds syntax highlighting, git gutter marks (when applicable), automatic paging (if the file is large), and in general, makes the output much more enjoyable to read.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://httpie.org/"&gt;httpie&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a1-f3SNy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-httpie.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a1-f3SNy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-httpie.gif" alt="httpie tool in action"&gt;&lt;/a&gt;httpie in action (source: &lt;a href="https://httpie.org/"&gt;https://httpie.org/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;If you need to send some HTTP requests and you find &lt;code&gt;curl&lt;/code&gt; unintuitive to use, try &lt;code&gt;httpie&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s an excellent alternative. It’s easier to use with sensible defaults and simple syntax, returns a colorized output, and even supports installing additional plugins (for different types of authentication).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://tldr.sh/"&gt;tldr&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Simplified man pages. &lt;em&gt;“man pages”&lt;/em&gt; contain manuals for Linux software that explain how to use a given command. Try running &lt;code&gt;man cat&lt;/code&gt; or &lt;code&gt;man grep&lt;/code&gt; to see an example. They are very detailed and sometimes can be difficult to grasp. So &lt;code&gt;tldr&lt;/code&gt; is a community effort to extract the essence of each man page into a brief description with some examples.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tldr&lt;/code&gt; works for the most popular software. As I said, it’s a community effort, and there is a slim chance that someone will document an obscure package for you. But when it works, the information it provides usually contains what you are looking for.&lt;/p&gt;

&lt;p&gt;For example, if you want to create a gzipped archive of a few files, &lt;code&gt;man tar&lt;/code&gt; will overwhelm you with the possible options. &lt;code&gt;tldr tar&lt;/code&gt; will instead list some common examples - the second one being exactly the thing that you want to do:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cAOKeY3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-tldr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cAOKeY3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-tldr.jpg" alt="man page vs tldr"&gt;&lt;/a&gt;man pages are great, but sometimes using tldr will be much faster to find a specific information&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://the.exa.website/"&gt;exa&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--okzf4ymi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-exa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--okzf4ymi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-exa.jpg" alt="exa in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;exa&lt;/code&gt; can be a replacement for the &lt;code&gt;ls&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;It’s colorful, displays additional information like the git status, automatically converts file size to human-readable units, and all that while staying equally fast to &lt;code&gt;ls&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Even though I like it and recommend it, for some reason, I still stick with &lt;code&gt;ls&lt;/code&gt; instead. Muscle memory, I guess?&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://litecli.com/"&gt;litecli&lt;/a&gt; and &lt;a href="https://www.pgcli.com/"&gt;pgcli&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dK2pgzbE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-litecli.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dK2pgzbE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-litecli.gif" alt="litecli in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My go-to CLI solutions for SQLite and PostgreSQL. With the auto-completion and syntax highlighting, they are much better to use than the default &lt;code&gt;sqlite3&lt;/code&gt; and &lt;code&gt;psql&lt;/code&gt; tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/mas-cli/mas"&gt;mas&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--szO0WPJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-mas.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--szO0WPJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-mas.jpg" alt="mas website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mas&lt;/code&gt; is a CLI tool to install software from the App Store. I used it once in my life - when I was setting up my Macbook. And I will use it to set up my next Macbook too.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mas&lt;/code&gt; lets you automate the installation of software in macOS. It saves you from &lt;em&gt;a lot&lt;/em&gt; of clicking. And, since you are reading an article about CLI tools, I assume that - just like me - you don’t like clicking.&lt;/p&gt;

&lt;p&gt;I keep a list of apps installed from the App Store in my “disaster recovery” scripts. If something bad happens, I hopefully should be able to reinstall everything with minimal hassle.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://dev.yorhel.nl/ncdu"&gt;ncdu&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2ln7RPeE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-ncdu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2ln7RPeE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-ncdu.jpg" alt="ncdu in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disk usage analyzer for the terminal. Fast and easy to use. My default tool when I need to free some space (&lt;em&gt;“Ohh, I’m sure that 256GB of disk space will be plenty!”&lt;/em&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  That’s all folks
&lt;/h2&gt;

&lt;p&gt;It was a long list, but hopefully, you discovered something new today.&lt;/p&gt;

&lt;p&gt;Some of the tools like the &lt;code&gt;fd&lt;/code&gt;, &lt;code&gt;ripgrep&lt;/code&gt;, or &lt;code&gt;httpie&lt;/code&gt; are improved versions of things that you probably already know. Except that the new versions are easier to use, they provide better output, and sometimes are actually faster. So don’t cling to old tools only because everyone else is using them.&lt;/p&gt;

&lt;p&gt;A common argument for sticking with the &lt;em&gt;“standard Linux tools”&lt;/em&gt; that I hear is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;But what if you need to log in to a Linux server and do some work there? You won’t have access to your fancy tools. It’s better to learn how to use tools that come built-in with most Linux distributions.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When was the last time you had to log in to a Linux server? One where you can’t install software, but you had to debug some issues manually? I don’t even remember. Not many people do that anymore. Maybe it’s time to rethink how you do the deployment and move away from manual work into something more scalable?&lt;/p&gt;

&lt;p&gt;Don’t let your tool-belt get rusty and add some new CLI tools there!&lt;/p&gt;




&lt;p&gt;Many of the tools that I mentioned are related to Python programming. If you want to learn more and see how I use them, I’ve made a free video for PyCon 2020 conference called &lt;em&gt;"Modern Python Developer's Toolkit"&lt;/em&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=WkUBx3g2QfQ"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ew1Gy3gt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-06-18-cli-pycon.jpg" alt=""&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;It's a two-hour-long tutorial on how to set up a Python development environment, which tools to use, and finally - how to make a TODO application from scratch (with tests and documentation). You can find &lt;a href="https://www.youtube.com/watch?v=WkUBx3g2QfQ"&gt;it on YouTube&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tools</category>
      <category>productivity</category>
      <category>cli</category>
      <category>mac</category>
    </item>
    <item>
      <title>18 Plugins for Writing Python in VS Code</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Mon, 27 Apr 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/18-plugins-for-writing-python-in-vs-code-144n</link>
      <guid>https://forem.com/switowski/18-plugins-for-writing-python-in-vs-code-144n</guid>
      <description>&lt;p&gt;VS Code is a great text editor. But when you install it, its functionality is limited. You can edit JavaScript and TypeScript, but for other programming languages, it will be just a text editor. You will need to add some plugins to turn it into a proper IDE.&lt;/p&gt;

&lt;p&gt;Luckily, when you open a file in a new language, VS Code will suggest an extension that can help you. With the Python extension, you can already do a lot - you get syntax highlighting, code completion, and many other features that turn a text editor into a code editor.&lt;/p&gt;

&lt;p&gt;But there are many other plugins that I discovered when working with Python. Some add entirely new functionality, and others offer just a small improvement here and there. I’ve decided to write them down. I hope some of you will find them useful!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-python.python"&gt;Python&lt;/a&gt; and other language-specific plugins
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G6PXX_cF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-python.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G6PXX_cF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-python.jpg" alt="Plugins: Python"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First and foremost - the Python plugin for VS Code. Out of the box, there is no support for Python in VS Code, but when you open a Python file, VS Code will immediately suggest this plugin. It adds all the necessary features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax highlighting for Python files&lt;/li&gt;
&lt;li&gt;Intellisense (code-completion suggestions)&lt;/li&gt;
&lt;li&gt;Ability to start a debugger&lt;/li&gt;
&lt;li&gt;Support for collecting and running tests (with different testing frameworks like pytest or unittest)&lt;/li&gt;
&lt;li&gt;Different linters&lt;/li&gt;
&lt;li&gt;And plenty of other small features that turn VS Code into a proper Python editor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it’s the same with different languages. Each time you open a file that VS Code doesn’t support, you get a suggestion of a plugin for that language. It’s a great approach! On the one hand, you don’t have to figure out which extensions you need to install, but on the other hand, you don’t slow down your IDE with plugins that you will never use.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=batisteo.vscode-django"&gt;Django&lt;/a&gt; and other framework-specific plugins
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZeHWXMyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-django.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZeHWXMyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-django.jpg" alt="Plugins: Django"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are working with frameworks, there is usually a plugin that will make your life easier, like &lt;a href="https://marketplace.visualstudio.com/items?itemName=batisteo.vscode-django"&gt;Django&lt;/a&gt; or &lt;a href="https://marketplace.visualstudio.com/items?itemName=cstrap.flask-snippets"&gt;flask-snippets&lt;/a&gt;. They bring some additional improvements for a given framework like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better syntax highlighting for framework-specific files (e.g., template files in Django that combine HTML with Django tags)&lt;/li&gt;
&lt;li&gt;Additional snippets - especially useful for the templating systems. Being able to insert loops and if-s with a two letter shortcut without opening and closing all those &lt;code&gt;{%&lt;/code&gt; tags is a blessing!&lt;/li&gt;
&lt;li&gt;Improved support for different functions. For example, Django plugin adds the ability to “Go to definition” from the templates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode"&gt;IntelliCode&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k05yiyNb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-intellicode.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k05yiyNb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-intellicode.gif" alt="Plugins: Intellicode"&gt;&lt;/a&gt;Source: &lt;a href="https://docs.microsoft.com/en-us/visualstudio/intellicode/intellicode-visual-studio-code"&gt;https://docs.microsoft.com/en-us/visualstudio/intellicode/intellicode-visual-studio-code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Intellicode makes the autocompletion a bit smarter. It tries to predict which term you are most likely to use in a given situation and puts that term at the top of the list (marked with a ☆ symbol).&lt;/p&gt;

&lt;p&gt;It works surprisingly well!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://docs.emmet.io/"&gt;Emmet&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jhl9_iyh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-emmet.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jhl9_iyh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-emmet.gif" alt="Plugins: Emmet"&gt;&lt;/a&gt;Source: &lt;a href="https://code.visualstudio.com/docs/editor/emmet"&gt;https://code.visualstudio.com/docs/editor/emmet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Technically, Emmet is not an extension because it’s already integrated with VS Code by default (due to its huge popularity). But it still deserves mention, in case there is someone who never heard about it.&lt;/p&gt;

&lt;p&gt;Emmet is going to be your best friend if you are writing a lot of HTML and CSS. It lets you expand simple abbreviations into full HTML, it adds CSS prefixes (together with vendor prefixes), and a whole bunch of other useful functions (rename a tag, balance in/out, go to matching pair, etc.)&lt;/p&gt;

&lt;p&gt;I absolutely love it when I need to write HTML. I started using it to quickly add a class to a tag (&lt;code&gt;div.header&lt;/code&gt; or &lt;code&gt;a.btn.btn-primary&lt;/code&gt;) and then I learned new features. With Emmet you can write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ul&amp;gt;li.list-item*3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and if you press Enter, it will turn into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring"&gt;Autodocstring&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n-g9y7b9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-autodocstring.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n-g9y7b9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-autodocstring.gif" alt="Plugins: Autodocstring"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This plugin speeds up writing Python documentation by generating some of the boilerplate for you.&lt;/p&gt;

&lt;p&gt;Write a function signature, type &lt;code&gt;"""&lt;/code&gt; to start the docstring, press Enter, and this plugin does the rest. It will take care of copying the arguments from the function signature to the docs. And if you add types to your arguments, it will recognize them and put them in the correct place in the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks"&gt;Bookmarks&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y__co46c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-bookmarks.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y__co46c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-bookmarks.jpg" alt="Plugins: Bookmarks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This extension lets you bookmark locations in your code, easily list all your bookmarks in a sidebar, and move between them with keyboard shortcuts.&lt;/p&gt;

&lt;p&gt;It’s incredibly useful then I’m digging into a new codebase (so I can jump around and not get lost). I also find it helpful when I’m trying to debug some complicated issues - VS Code has a functionality to “Go to Previous/Next location”, but without bookmarks, it’s easy to get lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=deerawan.vscode-dash"&gt;Dash&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_JHm9u8F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-dash.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_JHm9u8F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-dash.gif" alt="Plugins: Dash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Dash extension, you can access offline documentation for basically any programming language or framework.&lt;/p&gt;

&lt;p&gt;It requires installing one of the additional tool to provide the documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kapeli.com/dash"&gt;Dash for macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zealdocs.org/"&gt;Zeal for Linux/Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://velocity.silverlakesoftware.com/"&gt;Velocity for Windows&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you download the documentation, you can access it offline.&lt;/p&gt;

&lt;p&gt;I’m not using it very often, but it’s a great tool if you need to work without access to the Internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens"&gt;Error Lens&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MUu7oX1l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-error-lens.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MUu7oX1l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-error-lens.jpg" alt="Plugins: Error Lens"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes the errors marks in VS Code are hard to spot (especially the “info” hints). If you don’t wrap lines, it’s even worse - the error can be in the part of the code not visible on the screen.&lt;/p&gt;

&lt;p&gt;That’s why I’m using Error Lens. It lets me modify how the errors should be displayed. It can display the error message next to the line where it occurs and a Sublime-like error icons in the gutter (next to the line number).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=sleistner.vscode-fileutils"&gt;File Utils&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_iYDmSEd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-file-utils.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_iYDmSEd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-file-utils.jpg" alt="Plugins: File Utils"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This small plugin adds a few file-related commands to the Command Palette (normally you can perform them by right-clicking in the sidebar):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rename&lt;/li&gt;
&lt;li&gt;Move&lt;/li&gt;
&lt;li&gt;Duplicate&lt;/li&gt;
&lt;li&gt;Copy path or name of the file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also adds a “Move/Duplicate File” option to the context menu.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens"&gt;GitLens&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LtnWRCYX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-gitlens.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LtnWRCYX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-gitlens.gif" alt="Plugins: GitLens"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Massive plugin - adds a lot of git integration to VS Code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can show blame annotations per line, per file, in the status bar, or on hover.&lt;/li&gt;
&lt;li&gt;Provides you with context links to show changes, show diff, copy commit ID.&lt;/li&gt;
&lt;li&gt;Brings a sidebar with probably every possible information about the git repository, file and line history, compare and search menus, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s much more powerful than the default “source control” panel of VS Code. I don’t think I’m using even 20% of its features.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow"&gt;indent-rainbow&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zvcOi1FA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-indent-rainbow.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zvcOi1FA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-indent-rainbow.jpg" alt="Plugins: Indent Rainbow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Very helpful plugin for working with languages like Python, where indentation matters. Every level of indentation gets a slightly different color, so it’s easier to see at a glance where a given code block ends.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=wmaurer.vscode-jumpy"&gt;jumpy&lt;/a&gt; (or &lt;a href="https://marketplace.visualstudio.com/items?itemName=metaseed.metago"&gt;MetaGo&lt;/a&gt;)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BfD2fjHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-jumpy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BfD2fjHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-jumpy.gif" alt="Plugins: jumpy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;jumpy is a very peculiar plugin that takes some time to get used to. Basically, it’s supposed to help you move around your code faster.&lt;/p&gt;

&lt;p&gt;If you press a keyboard shortcut, jumpy will display a 2-letter code next to every word on the screen. If you type those two letters, your cursor will jump to that location. Similar to what you can do with vim in “normal” mode (with less typing).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=Rubymaniac.vscode-paste-and-indent"&gt;Paste and Indent&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oxyfiGpF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-paste-indent.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oxyfiGpF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-paste-indent.jpg" alt="Plugins: Paste and Indent"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find that VS Code is not doing a good job when you paste code, try this extension. It will let you assign a “Paste and Indent” action to any key shortcut. This command will do its best to indent the code correctly after you paste it (to match the surrounding code). I’m using the “Command+Shift+V” shortcut for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=alefragnani.project-manager"&gt;Project Manager&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GKxTq-gK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-project-manager.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GKxTq-gK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-project-manager.jpg" alt="Plugins: Project Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VS Code supports the concept of workspaces - you can group some files and folders together and easily switch between them. But you still need to save the workspace configuration, and sometimes it can get lost - I either accidentally remove it or forget where I saved it.&lt;/p&gt;

&lt;p&gt;Project Manager takes this hassle away. You can save projects and then open them, no matter where they are located (and you don’t have to worry about storing the workspace preference files). Also, it adds a sidebar to browse all your projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbankier.vscode-quick-select"&gt;Quick and Simple Text Selection&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PtUQXyAm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-simple-text-selection.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PtUQXyAm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-simple-text-selection.jpg" alt="Plugins: Quick and Simple Text Selection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I like to use shortcuts that let me select all the text in brackets, tags, etc. By default, VS Code has command to “Expand/Shrink selection” that works ok-ish, but I found the Quick and Simple Text Selection plugin to be a much better way.&lt;/p&gt;

&lt;p&gt;It adds a few new shortcuts to select text in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;single/double quotes&lt;/li&gt;
&lt;li&gt;parentheses&lt;/li&gt;
&lt;li&gt;square/angular/curly brackets&lt;/li&gt;
&lt;li&gt;tag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tried to map them to some intuitive shortcuts and they work like a charm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Command + ‘ (⌘ + ‘) - select text in single quotes&lt;/li&gt;
&lt;li&gt;Command + “ (⌘ + ⇧ + ‘)- select text in double quotes&lt;/li&gt;
&lt;li&gt;Command + ( (⌘ + ⇧ + 9)- select text in parentheses&lt;/li&gt;
&lt;li&gt;Command + &amp;lt; (⌘ + ⇧ + ,)- select text in tag&lt;/li&gt;
&lt;li&gt;Command + , (⌘ + ,)- select text in angular brackets&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/scottw/vscode-settings-sync-4d27"&gt;Settings Sync&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kxLL4pZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-settings-sync.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kxLL4pZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-settings-sync.jpg" alt="Plugins: Settings Sync"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s not really related to Python, but it’s a very important plugin, so I wanted to mention it.&lt;/p&gt;

&lt;p&gt;Settings Sync lets you save the VS Code settings to a private GitHub gist, so you can easily restore them if you switch to a different computer (or if you lose/destroy your current one).&lt;/p&gt;

&lt;p&gt;In one of the upcoming versions of VS Code, settings synchronization will become built-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=wayou.vscode-todo-highlight"&gt;TODO Highlight&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dr4TIePL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-todo-highlight.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dr4TIePL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-todo-highlight.jpg" alt="Plugins: TODO Highlight"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Highlights all TODO/FIXME/NOTE in the code, so you can easily spot them. You can easily customize it by adding new words and changing the highlight style.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=ban.spellright"&gt;Spell Right&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nz3SKEop--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-spell-right.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nz3SKEop--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-04-27-plugins-spell-right.jpg" alt="Plugins: Spell Right"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s strange, but VS Code doesn’t have a built-in spell checker. So you have to install one as an extension.&lt;/p&gt;

</description>
      <category>python</category>
      <category>vscode</category>
    </item>
    <item>
      <title>My Favorite Macbook Tools</title>
      <dc:creator>Sebastian Witowski</dc:creator>
      <pubDate>Thu, 09 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/switowski/my-favorite-macbook-tools-2apf</link>
      <guid>https://forem.com/switowski/my-favorite-macbook-tools-2apf</guid>
      <description>&lt;p&gt;I could spend days just tweaking things on my computer. Actually, I do. Whenever I see something annoying, I want to drop everything and try to fix it right away. It can be anything. From a minor: &lt;em&gt;“Hey, I just run the same command twice, I should create an alias!”&lt;/em&gt; to installing random tools (&lt;em&gt;“Hmm, I’m wondering if there is a way to get notifications when a long-running job in a terminal finishes, so I can do other stuff in the meantime?”&lt;/em&gt;). I also love to read what tools other people are using. This time, I’ve decided to share mine.&lt;/p&gt;

&lt;p&gt;Since this is a very long post, I’ve to split it into two parts: the Mac apps and the CLI stuff (coming soon!).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.alfredapp.com/"&gt;Alfred&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xWNYpbws--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-alfred.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xWNYpbws--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-alfred.jpg" alt="Alfred"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alfred is a launcher - this app that opens when you press ⌘+Spacebar and lets you quickly open other apps. On steroids. And I absolutely love it! I knew about its existence even before owning a Mac, and I envied Mac users to have such a great launcher. I used &lt;a href="https://www.launchy.net/"&gt;Launchy&lt;/a&gt; on Windows and &lt;a href="https://en.wikipedia.org/wiki/GNOME_Do"&gt;GNOME Do&lt;/a&gt; on Linux, and they are both great (just having a launcher will make you feel much more productive).&lt;/p&gt;

&lt;p&gt;So when I got my Mac, one of the first things I did was to replace Spotlight with Alfred. Even without the PowerPack, it’s much more powerful than Spotlight. But the PowerPack (a paid extension) is where it really packs a punch. It enables a few more features like a text expansion (here called “snippets”) or the clipboard history. But the best features that come with the Powerpack are the “Alfred Workflows” - user-defined scripts that let you automate many tasks. I have a bunch of workflows to quickly create reminders, to-do tasks, temporary emails, convert currency, or search for emoji and Unicode characters.&lt;/p&gt;

&lt;p&gt;There is already version 4 of Alfred, but I still haven’t upgraded from version 3.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Alfred workflows
&lt;/h3&gt;

&lt;p&gt;Here are some of the workflows that I’m using with Alfred:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/moranje/alfred-workflow-todoist"&gt;Alfred Workflow Todoist&lt;/a&gt; - lets me quickly add tasks to Todoist&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.packal.org/workflow/convert"&gt;Convert&lt;/a&gt; - convert from one unit to another. I use it mostly for currencies.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ihowson/EggTimer2"&gt;EggTimer&lt;/a&gt; - a bit outdated workflow, but the only one that lets me easily create timers (I want to take a short break every 60 minutes of work)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jsumners/alfred-emoji"&gt;Emoji search&lt;/a&gt; - much faster emoji search than the default emoji icon panel on Mac.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/surrealroad/alfred-reminders"&gt;Reminders for Alfred 3&lt;/a&gt; - typing “r do stuff in 20 minutes” will create a reminder “do stuff” 20 minutes from now. It lets me quickly set reminders without getting distracted from the current task.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.packal.org/workflow/temporaryemail"&gt;TemporaryEmail&lt;/a&gt; - create a disposable temporary email when I need to register in some spammy service.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jason0x43/alfred-toggl"&gt;Toggl&lt;/a&gt; - interact with &lt;a href="https://toggl.com/"&gt;Toggl&lt;/a&gt; time tracker&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.packal.org/workflow/symbols-search"&gt;Unicode Symbol Search&lt;/a&gt; - quickly find and copy any Unicode symbol.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://magnet.crowdcafe.com/"&gt;Magnet&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TJeVwwzv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-magnet.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TJeVwwzv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-magnet.jpg" alt="Magnet windows manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Magnet is a window manager. It’s unbelievable that Mac still doesn’t have a built-in window manager that supports keyboard shortcuts. Luckily, Magnet fills this gap (for a price of a few $). By default, it uses ⌃+⌥ as a modifier, which conflicts with a lot of my VS Code settings (I’m using ⌃+⌥ as a modifier for all my custom shortcuts). So I changed it to ⌘+⌥+⌃. It sounds like a lot of keys to press simultaneously, but I mapped all 3 of them to 1 key using Karabiner-Elements (mentioned below).&lt;/p&gt;

&lt;p&gt;If you prefer a free alternative, there is also &lt;a href="https://github.com/rxhanson/Rectangle"&gt;Rectangle&lt;/a&gt; - a successor of the &lt;a href="https://www.spectacleapp.com/"&gt;discontinued Spectacle app&lt;/a&gt;. I started using Magnet because Spectacle didn’t have some shortcuts (like “Split into 1/3 of the screen”). But Rectangle has even more options than Magnet, so it looks like an excellent replacement.&lt;/p&gt;

&lt;p&gt;If you miss a tiling window manager, there is also &lt;a href="https://github.com/ianyh/Amethyst"&gt;Amethyst&lt;/a&gt;. I tried it, but it didn’t feel as good as i3, so I didn’t stick with it. It was missing the basic functionality, like moving into a specific direction with a &lt;code&gt;modifier + arrow key&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://todoist.com/"&gt;Todoist&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MNL_0Bvt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-todoist.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MNL_0Bvt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-todoist.jpg" alt="Todoist task manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My to-do list manager. I tried using different apps (gosh, I think I’ve tested all of them), and I was always coming back to Todoist. I like it for its clean interface, the “Next 7 days” view, and the ability to use special shortcuts when creating tasks. For example, if I add a task: “Write a blog post tod p1 #blog” it will create a “Write a blog post” task, set due date to today, add “Priority 1” flag, and assign it to a project called “Blog.” Neat!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://dev.to/scottw/managing-my-menu-bar-10od-temp-slug-5077059"&gt;Dozer&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is how my menu bar looks like most of the time:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8F1kWrsw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-how-my-menu-bar-looks-like.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8F1kWrsw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-how-my-menu-bar-looks-like.jpg" alt="How my menu bar looks like"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how it &lt;strong&gt;really&lt;/strong&gt; looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fmax5dmP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-how-my-menu-bar-really-looks-like.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fmax5dmP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-how-my-menu-bar-really-looks-like.jpg" alt="How my menu bar really looks like"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dozer is a small application that keeps my menu bar manageable. It lets me hide those items that I’m not using very often. And with a keyboard shortcut, I can quickly show/hide them. If you need more features, there is also a paid app called &lt;a href="https://www.macbartender.com/"&gt;Bartender&lt;/a&gt;, but Dozer works like a charm for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://folivora.ai/"&gt;BetterTouchTool&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--du1mMeY1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-btt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--du1mMeY1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-btt.jpg" alt="BetterTouchTool"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BetterTouchTool (or BTT for short) lets you customize any input device for your Mac. You can create custom Trackpad or Magic Mouse gestures, custom keyboard shortcuts, and macros (sequences of keys). It comes with a clipboard manager and window manager. It could replace a bunch of other tools that I’m using.&lt;/p&gt;

&lt;p&gt;But none of those features are the reason why I decided to pay $10 for this app. I bought it so I can customize my Macbook’s touch bar. In its original form, the touch bar was unusable for me. Buttons that change depending on which application you are using is one of the worst ideas ever. I don’t want to rediscover what’s on the touch bar each time I change the currently used app. And half of the buttons are useless anyway. I don’t want a slider for the volume or brightness! 99% of the time, I only need to increase or decrease it by a notch. I should be able to do this with one press of a button. So, as most of the people that I know, I didn’t even use the touch bar. And one day, I stumbled upon an article on HackerNews that was explaining &lt;a href="http://vas3k.com/blog/touchbar/"&gt;how to make the touch bar actually useful&lt;/a&gt;. After reading this article, I immediately bought the BTT.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--khri-W7_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-my-touchbar.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--khri-W7_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-my-touchbar.jpg" alt="My touch bar"&gt;&lt;/a&gt;This is how my touch bar looks like today&lt;/p&gt;

&lt;p&gt;Now on my touch bar, there are (from left to right):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Esc button (that I don’t really use, but you can’t remove it).&lt;/li&gt;
&lt;li&gt;A special button that will switch the touch bar back to the default, crappy Mac version (again, it’s there by default, and I don’t use it).&lt;/li&gt;
&lt;li&gt;Buttons that open (or switch to) the Calendar, Mail, Todoist, and Toggl apps. I use them from time to time (mostly when I’m working with Macbook on my laps, otherwise it’s faster for me to switch with the keyboard).&lt;/li&gt;
&lt;li&gt;A script that shows the currently playing song from Spotify or Youtube. It’s only visible when one of those apps is open. If I press the name of the song, it will skip to the next one. Best button! Ever!&lt;/li&gt;
&lt;li&gt;Play/Pause button for Spotify/Youtube.&lt;/li&gt;
&lt;li&gt;Brightness down and up buttons.&lt;/li&gt;
&lt;li&gt;Volume down, up, and mute buttons.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a ton of other widgets that you can use - weather, temperature, time and date, custom shell, or Apple Scripts. The BTT author has added a lot of new ones since I set up my touch bar, so I will have to revise it at some point.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://meetfranz.com/"&gt;Franz&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d6reIpSS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-franz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d6reIpSS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-franz.jpg" alt="Franz app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One app to aggregate all my messaging apps. It’s a great, free tool that supports most of the messaging apps. I’m using it with Slack, Skype, WhatsApp, Telegram, Twitter, Hangouts, and Facebook Messenger. The only downside is - each service that you add spins a new electron app. The memory consumption can go through the roof sometimes. But so far, I haven’t found any reliable alternative.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://justgetflux.com/"&gt;Flux&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tOa2crzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-flux.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tOa2crzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-flux.jpg" alt="Flux app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flux adapts the color of the screen to the time of the day. Early in the day or late in the night (for most of us, programmers, it’s usually the same thing), it will make the colors of your computer warmer (yellow). It’s one of those applications that you install once, and you forget about it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://pqrs.org/osx/karabiner/index.html"&gt;Karabiner-Elements&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G7WBX9Sf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-karabiner.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G7WBX9Sf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-karabiner.jpg" alt="Karabiner-Elements app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Karabiner-Elements is a free app to customize your keyboard (it has a lot of duplication with BetterTouchTool). You can swap keys, change the behavior of function keys, or define some complex macros. I use it for three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I have swapped the Caps Lock and Esc keys (and I can’t believe how I could live before doing it). Luckily, I discover this trick before I switched to Macbook with its useless Esc key on the touch bar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since I’m using &lt;a href="https://kinesis-ergo.com/shop/advantage2/"&gt;Kinesis Advantage 2 keyboard&lt;/a&gt;, I have remapped the Home key to ⌃(Ctrl) and End to a combination of ⌘+⌥+⌃ (which is a modifier that I use for window management with Magnet). This combination (often with the addition of Shift) is called the &lt;a href="https://statusq.org/archives/2016/09/25/7857/"&gt;“Hyper” key&lt;/a&gt;, and it gives you another modifier key to use with your shortcuts. Just in case having three modifier keys on Mac is not enough.&lt;br&gt;
If you are using a &lt;em&gt;“normal”&lt;/em&gt; keyboard, I saw people mapping “pressing Ctrl” to act like the Esc key and “holding Ctrl” to act like the Ctrl key. Which is pretty smart, since we never just press Ctrl - it’s always used in combination with other keys. This will free up the “Esc” (or “Caps Lock” if you followed my previous advice and swapped them) for your Hyper key or any kind of crazy combination that you can think of.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A careful reader might notice that I no longer have a way to enable the Caps Lock. The “Caps Lock” key is now my Esc, and Macbook doesn’t let you redefine the “Esc” key on the touch bar. So I have 2 Esc keys and no Caps Lock. Which is bad - how am I supposed to argue with people on the Internet without the Caps Lock? Luckily, there was a solution for that - I mapped pressing &lt;strong&gt;both Shift&lt;/strong&gt; keys simultaneously to Caps Lock. It sounds crazy, but it’s actually quite intuitive and easy to remember. I have no idea why it’s not a default behavior. To make it work, I had to modify the Karabiner-Elements config file directly. You can &lt;a href="https://github.com/switowski/dotfiles/blob/master/.config/karabiner/karabiner.json#L41"&gt;check my dotfiles repository&lt;/a&gt; to see how to do this. Just remove the stuff that you don’t need and put this &lt;code&gt;karabiner.json&lt;/code&gt; in your &lt;em&gt;~/.config/karabiner/karabiner.json&lt;/em&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://fishshell.com/"&gt;Fish&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g5jTUttF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-fish.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5jTUttF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-fish.jpg" alt="Fish shell"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I first started using Linux, I used Bash. Then I switched to Z shell. With oh-my-zsh. Then I realized that it’s becoming a bit sluggish, mostly during the startup time. I decided that I don’t have time to sit down and stare at my screen for almost 2 seconds waiting for it to load. So I switched to prezto when it was a cool, new thing (and not an &lt;a href="https://github.com/sorin-ionescu/prezto/issues/1239"&gt;abandoned package as it is today&lt;/a&gt;). In 2017 I switched to a Macbook. Which was a great opportunity to try something new! Some of my colleagues at work were using fish at that time, and it looked interesting. It had some neat features out of the box, like the autosuggestions, syntax highlighting, or switching between directories using ⌥+→ and ⌥+←. So I tried it. And I liked it, so I’m still using it today. I use &lt;a href="https://github.com/jorgebucaran/fisher"&gt;fisher&lt;/a&gt; package manager with a few plugins (there is also oh-my-fish, but fisher was supposed to be faster, so I stick to it). I try to avoid adding too many plugins to make the terminal startup faster (I’m starting a bunch of terminal sessions on an average day, so those seconds add up).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="http://captin.mystrikingly.com/"&gt;Captin&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ROBhoUul--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-captin.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ROBhoUul--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-captin.jpg" alt="Captin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It shows you a notification when you turn the Caps Lock on or off. Since I’m using a slightly unusual way to operate the Caps Lock, I like to have this little visual (and audio) cue when I turn it off.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://apps.ankiweb.net/"&gt;Anki&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Grv8CX6J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-anki.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Grv8CX6J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-anki.jpg" alt="Anki"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anki is a spaced repetition app. For most of my life I thought that I was stupid because I had terrible problems with memorizing things. It turns out that I was using the wrong technique. Spaced repetition is a simple mechanism that makes sure that you don’t repeat things that you already remember, but only those things that you have problems remembering. I’m using Anki purely on my phone, but adding new words is much easier to do on a computer.&lt;/p&gt;

&lt;p&gt;Also, Anki is not only useful for learning new languages (although I’m using it right now for learning Japanese)! I’ve meet someone who started working on a very complicated software project a few months ago. There were so many parts of the system that it would take him (and every new person on the team) a long time to learn how they all work. So he started creating flashcards with Anki describing various parts of the system and how to use them. Soon, other people from his team started using Anki as well, saying that it’s much better than reading pages and pages of documentation!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.grammarly.com/"&gt;Grammarly&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XMTiAxXv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-grammarly.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XMTiAxXv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-grammarly.jpg" alt="Grammarly"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Grammarly is an app that checks the grammar and typos in your text. As a non-native speaker, it helps me a lot to find some things that I wouldn’t probably notice. I used the free version for a few months, and a few days ago, I finally bought the premium plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://evernote.com/intl/en/products/skitch"&gt;Skitch&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zU0Rb5AL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-skitch.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zU0Rb5AL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-skitch.jpg" alt="Skitch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Taking screenshots on Mac is easy. There are keyboard shortcuts that let you quickly take a screenshot of the whole desktop, just a part of it, or even choose some options, like a delayed screenshot, if you want a screenshot of a hover effect on a website. Then you can open the screenshot, put some arrows or text, and send it to someone. So that worked for me for a long time. Until I found Skitch. With Skitch, when you take a screenshot, it automatically opens a simple app with some default tools for annotating them. It has huge pink arrows, large text, etc. When you finish, you can just drag and drop the screenshot from the app to a chat window. No more opening the Finder to find the screenshot, no more adjusting the text size and color in Preview app or clumsy arrows that you make from 3 straight lines glued together. With Skitch I can take a screenshot, annotate the problem and send it to someone on a Slack in less than 10 seconds.&lt;/p&gt;

&lt;p&gt;The only downside is that Sketch’s keyboard shortcuts are non-customizable and the conflict with Mac’s ones. I ended up changing Mac’s shortcuts.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/keycastr/keycastr"&gt;KeyCastr&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u6S-web1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-keycastr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u6S-web1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-keycastr.jpg" alt="KeyCastr"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you ever wondered what app people use in their videos to show which keys they are pressing, then KeyCastr is one of those apps. I have used it literally once, but if I ever need to record some screencast, I will definitely use it again.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.cockos.com/licecap/"&gt;LICEcap&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Fp6-1GP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-licecap.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Fp6-1GP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-licecap.jpg" alt="LICEcap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They say that a picture is worth a thousand words. Well, a GIF is probably worth over 9000 words. Especially if you are trying to show some buggy behavior. LICEcap is a free software to quickly record GIFs from your screen. I love it!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://simplemind.eu/"&gt;SimpleMind Lite&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LGCIDkuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-simplemind.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LGCIDkuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-simplemind.jpg" alt="SimpleMind Lite"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tool for mind mapping.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://theunarchiver.com/"&gt;The Unarchiver&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BUTfq4Nj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-unarchiver.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BUTfq4Nj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-unarchiver.jpg" alt="The Unarchiver"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I mostly use its CLI command &lt;code&gt;unar&lt;/code&gt; to extract any kind of archive. It’s so good not have to remember the &lt;code&gt;tar&lt;/code&gt; or &lt;code&gt;unzip&lt;/code&gt; flags anymore!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---1JpnTzb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-vscode.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---1JpnTzb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-vscode.jpg" alt="Visual Studio Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My code editor. I used Sublime for many years, but when I was switching to a Macbook, I decided to finally move to VS Code (after two unsuccessful attempts to do it in the past).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://workflowy.com/"&gt;Workflowy&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D8PuscKX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-workflowy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D8PuscKX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://switowski.com/assets/img/posts/img_2020-01-09-workflowy.jpg" alt="Workflowy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use it to organize my long- and short-term goals that I later convert into actionable steps in Todoist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mundane but mandatory stuff
&lt;/h2&gt;

&lt;p&gt;There are also a bunch of tools that probably everyone knows, but to keep this list complete, here they are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://evernote.com/"&gt;Evernote&lt;/a&gt; - a note-taking app that doesn’t need an introduction. It doesn’t get much love nowadays, but I never actually moved away from it. The free plan is still perfectly fine for me.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://git-scm.com/"&gt;git&lt;/a&gt; - THE version control tool.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.vim.org/"&gt;vim&lt;/a&gt; - I use vim mostly for quick edits (small files or notes that don’t require a full-fledged IDE).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.videolan.org/vlc/index.html"&gt;VLC&lt;/a&gt; - video player. I used it on all my Mac, Windows, and Linux computers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.gimp.org/"&gt;GIMP&lt;/a&gt; - I should probably learn how to use one of those hip, beautiful (and expensive) Mac apps for editing images. But I’m too lazy, and I know GIMP for years, so it’s fine for now.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://inkscape.org/"&gt;Inkscape&lt;/a&gt; - see above.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.dropbox.com/"&gt;Dropbox&lt;/a&gt; - my preferred file synchronization tool.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://iterm2.com/"&gt;iterm2&lt;/a&gt; - &lt;em&gt;de facto&lt;/em&gt; terminal app for Mac. It works great, stores its configuration nicely in my Dropbox backup folder, and I’m not planning to change it to anything else any time soon.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt; - even though I have not used it in ages, it’s my go-to virtualization tool. Back in the days, when I was still using Windows at home, VirtualBox was a great tool to easily spin up Linux containers, so I could actually do some programming (this was before Windows 10 with all the programming goodies).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dotfiles. I’m a big fan of dotfiles, and while there are some great tools to manage them (like &lt;a href="https://yadm.io/#"&gt;yadm&lt;/a&gt; or &lt;a href="https://github.com/anishathalye/dotbot/"&gt;dotbot&lt;/a&gt;), I’ve always used some bash scripts to manage them. I try to keep them up to date, so in case of a laptop failure, I can switch to a new one with all my settings and software. You can find my dotfiles &lt;a href="https://github.com/switowski/dotfiles"&gt;here&lt;/a&gt;.&lt;br&gt;
If you are using Mac or Linux and you don’t have dotfiles, but you would like to preserve the configuration of your software, there is also &lt;a href="https://github.com/lra/mackup"&gt;mackup&lt;/a&gt;. It will back up the configuration of most of your apps, including git, bash, etc. (it supports &lt;a href="https://github.com/lra/mackup#supported-applications"&gt;over 450 applications&lt;/a&gt;) into a folder in Dropbox, iCloud, or a similar service. Plus, it will replace the configuration files with symlinks to the backup location so that they will be automatically backed up. And with just one command, you can recover it on a different machine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/docker-for-mac/"&gt;Docker for Mac&lt;/a&gt; - Mac client for Docker. Before I had a Mac, I was jealous of the beautiful Docker interface with the Kitematic app that Mac users had. Downloading images, listing running containers, or executing commands inside them through a nice GUI sounded like a sweet option. By the time I switched to a Mac, I already knew how to use Docker enough that I was doing all those things comfortably in the terminal. So I actually never used the GUI (but if you are a &lt;em&gt;“GUI person”&lt;/em&gt;, I’m sure you are going to love it!).&lt;br&gt;
To make it even worse, Docker for Mac has a &lt;a href="https://github.com/docker/for-mac/issues/3232"&gt;memory leeks problem&lt;/a&gt;. It’s something that has been reported already in 2018 and closed because &lt;em&gt;“It’s a problem with Mac not with Docker.”&lt;/em&gt; Even though people are still regularly commenting that they have this issue. Even with stopped containers, after running for a while, Docker starts consuming quite a lot of memory (and its energy consumption is high, so I usually disable it when I’m on a battery).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tools that I don’t use, but I can highly recommend
&lt;/h2&gt;

&lt;p&gt;Finally, there are some great tools that I currently don’t use. Some of them are the tools that I used in the past. And for some of them, I still haven’t found a good use case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/tmux/tmux"&gt;tmux&lt;/a&gt; - I’ve tried to use this terminal multiplexer a few times. But I never stuck to it. It’s easy to define shortcuts in iterm2 to split terminals and move withing them, so I don’t really need a separate tool for this. I also don’t need to keep my terminal sessions running for ages. If a terminal &lt;em&gt;dies&lt;/em&gt;, it’s fine - I just start a new one. The obvious reason to use tmux is when you regularly connect to multiple servers. Which I no longer do. With Docker and the whole CI/CD movement, I realized that I less and less have to SSH anywhere. I work on my local machine, push my code to GitHub\GitLab, and the machines take over from there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://getbitbar.com/"&gt;BitBar&lt;/a&gt; - a great little app that lets you put the output from any script or program in your menu bar. CPU usage graphs? Checked. Little menulet to interact with MySQL? Docker status? Bitcoin price? Ten different battery level indicators or your Apple keyboard battery level indicator? All checked! The only reason why I don’t use it is - I don’t need any of those scripts for now. I only needed to show the RAM and CPU graphs, but unfortunately, there were no RAM graph plugins when I checked, so I installed &lt;a href="https://member.ipmu.jp/yuji.tachikawa/MenuMetersElCapitan/"&gt;MenuMeters&lt;/a&gt; instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/amphetamine/id937984704?mt=12"&gt;Amphetamine&lt;/a&gt; - prevents your Macbook from going to sleep. Good tool when you are giving presentations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kapeli.com/dash"&gt;Dash&lt;/a&gt; - a great tool to access the documentation. Supports probably every programming language ever invented, works offline, and you can even search directly on StackOverflow. But for some reason, I never actually get used to it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/flycut-clipboard-manager/id442160987?mt=12"&gt;Flycut&lt;/a&gt; a clipboard manager that I used for some time before switching to Alfred. It’s free.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.sublimetext.com/"&gt;Sublime Text&lt;/a&gt; - I’ve used it as my default programming editor for years. Then I switched to VS Code. I still keep Sublime Text to quickly preview and edit large files (although I use vim more and more for this).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dteoh/SlowQuitApps"&gt;Slow Quit Apps&lt;/a&gt; - it’s frustrating when I accidentally press ⌘+Q instead of ⌘+W, and I close the current app. Slow Quit App prevents this by adding a time threshold. By default you need to keep pressing ⌘+Q for 1 second to actually close an app. I would probably use it if I knew about it before, but since I didn’t, I decided to remap ⌘+Q to some useless command. So now it’s “inverting colors.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://mediaatelier.com/CheatSheet/"&gt;CheatSheet&lt;/a&gt; - I was really excited when I found this small helper. When you install it, each time you hold the ⌘ key, it will display an overlay with shortcuts for the current application. But then I realized that I don’t really use it, so I uninstalled it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I learned about most of those tools from my colleagues, browsing through dotfiles of other people, and reading posts on HackerNews. Without other people sharing their tools, I would probably still be using Nano. So I hope that someone will find this list useful and create an even better one on top of it!&lt;/p&gt;

&lt;p&gt;Preparing those lists was also an excellent opportunity to clean up my Mac a bit - I removed a bunch of unused applications, scripts, and aliases.&lt;/p&gt;

&lt;p&gt;Stay tuned for the second part, where I will talk about my favorite CLI tools!&lt;/p&gt;




&lt;p&gt;Photo by Lachlan Donald on &lt;a href="https://unsplash.com/photos/YVT5aF2QM7M"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mac</category>
      <category>tools</category>
    </item>
  </channel>
</rss>
