<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet href="/rss-style.xsl" type="text/xsl"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Python on Aleš Kancilija</title><link>https://aleskancilija.com/tags/python/</link><description>Recent content in Python on Aleš Kancilija</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 02 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://aleskancilija.com/tags/python/index.xml" rel="self" type="application/rss+xml"/><item><title>Your Analytics Dashboard Is Missing Half the Story</title><link>https://aleskancilija.com/posts/analytics-dashboard-missing-half-the-story/</link><pubDate>Mon, 02 Mar 2026 00:00:00 +0000</pubDate><guid>https://aleskancilija.com/posts/analytics-dashboard-missing-half-the-story/</guid><description>Serve Fathom Analytics from your own domain using Bunny.net CDN as a reverse proxy. Bypass ad-blockers, recover 42% of lost visitor data for ~$1/month.</description><content:encoded><![CDATA[<p>42% of your visitors are invisible.</p>
<p>That&rsquo;s how many people use ad-blockers or privacy-focused browsers. And if you&rsquo;re using privacy-first analytics like Fathom, these tools still block the official tracking script from <code>cdn.usefathom.com</code>—even though you&rsquo;re not collecting personally identifiable data.</p>
<p>I ran into this exact problem. My analytics dashboard showed decent traffic, but I knew the real numbers were higher. Ad-blockers were killing Fathom&rsquo;s script before it could fire. Every blocked request meant lost data about site visits and how people actually use my site.</p>
<p>Here&rsquo;s how I fixed it using Bunny.net&rsquo;s CDN as a reverse proxy. The solution bypasses blockers completely, costs about $1/month (even for multiple sites), and takes about 10 minutes to set up.</p>
<h2 id="the-problem-domain-based-blocking-kills-analytics">The Problem: Domain-Based Blocking Kills Analytics</h2>
<p>Ad-blockers work by maintaining lists of blocked domains. When your browser tries to load a script from <code>cdn.usefathom.com</code>, the blocker checks its list, finds a match, and blocks the request.</p>
<p>It doesn&rsquo;t matter that <a href="https://usefathom.com/ref/VOSDJB">Fathom</a> is privacy-first or GDPR-compliant. The blocker sees &ldquo;analytics domain&rdquo; and kills it.</p>
<p>This affects more than just Fathom. Plausible, Umami, and any third-party analytics service face the same issue. Even if you&rsquo;re doing everything right from a privacy perspective, your scripts get caught in the same net as invasive trackers.</p>
<p>The result? Your analytics dashboard becomes less useful. You can&rsquo;t make informed decisions about content, UX improvements, or conversion optimization when you&rsquo;re missing data from 42% of your visitors.</p>
<h2 id="what-didnt-work-dns-url-forwarding">What Didn&rsquo;t Work: DNS URL Forwarding</h2>
<p>My first attempt seemed logical: use URL forwarding at my DNS registrar to point <code>ana.mydomain.com</code> to <code>cdn.usefathom.com</code>.</p>
<p>The idea was simple. If I could serve the script from my own domain, ad-blockers wouldn&rsquo;t recognize it as a tracking script. First-party requests from your own domain don&rsquo;t appear on blocklists.</p>
<p>I tried this with my DNS provider at the time (freedns.si). It didn&rsquo;t work.</p>
<p><strong>Here&rsquo;s why URL forwarding failed for me:</strong></p>
<p>URL forwarding only worked with root URLs. When I requested <code>ana.mydomain.com/script.js</code>, the path got forwarded—but the slash disappeared. The request ended up going to <code>cdn.usefathom.comscript.js</code> (notice the missing slash between the domain and filename).</p>
<p>There was no way to configure it as <code>cdn.usefathom.com/{path}</code> or anything similar. At least not with freedns.si. I don&rsquo;t know if this is a limitation across all DNS providers, but it was a dealbreaker for me.</p>
<p>DNS forwarding simply doesn&rsquo;t handle URL paths properly. I needed something smarter.</p>
<h2 id="the-solution-reverse-proxy-through-bunnynet-cdn">The Solution: Reverse Proxy Through Bunny.net CDN</h2>
<p>The fix is to use a CDN as a reverse proxy. When someone requests <code>ana.mydomain.com/script.js</code>, the CDN fetches the file from <code>cdn.usefathom.com/script.js</code> and serves it under your domain.</p>
<p>This is proper proxying, not just URL forwarding. The full URL path is preserved correctly, and browsers only see your first-party domain.</p>
<p>I use <a href="https://bunny.net?ref=cklqznj7qp">Bunny.net</a> for this. It&rsquo;s a CDN provider with simple configuration at low cost.</p>
<p><strong>Here&rsquo;s what makes this work:</strong></p>
<p>Bunny.net creates a &ldquo;Pull Zone&rdquo;—a CDN endpoint that fetches content from an origin server. You point the Pull Zone at <code>cdn.usefathom.com</code>, configure a custom hostname like <code>ana.mydomain.com</code>, and the CDN handles the rest.</p>
<p>When a visitor loads your page:</p>
<ol>
<li>Their browser requests <code>https://ana.mydomain.com/script.js</code></li>
<li>Bunny&rsquo;s CDN receives the request</li>
<li>The CDN fetches the script from <code>https://cdn.usefathom.com/script.js</code></li>
<li>The CDN serves it to the visitor under your domain</li>
<li>Ad-blockers see a first-party request and allow it</li>
</ol>
<p>The visitor&rsquo;s browser never directly contacts <code>cdn.usefathom.com</code>. All communication goes through your domain.</p>
<h2 id="step-by-step-setup">Step-by-Step Setup</h2>
<p><em>Want to skip the manual setup? Jump to <a href="#automating-the-entire-setup">Automating the Entire Setup</a> for a programmatic approach.</em></p>
<h3 id="1-create-a-bunnynet-pull-zone">1. Create a Bunny.net Pull Zone</h3>
<p>Sign up for <a href="https://bunny.net?ref=cklqznj7qp">Bunny.net</a> (they have a 14-day free trial). Navigate to the Pull Zones section and create a new zone. Give it a descriptive name like <code>ana-mydomain</code>—this name determines your Bunny CDN hostname (<code>ana-mydomain.b-cdn.net</code>), which you&rsquo;ll need in step 3.</p>
<p><strong>Key settings:</strong></p>
<ul>
<li><strong>Origin URL</strong>: <code>https://cdn.usefathom.com</code></li>
<li><strong>Origin Host Header</strong>: <code>cdn.usefathom.com</code></li>
</ul>
<p>That second setting is important. Bunny marks it as optional, and technically if you leave it blank, Bunny extracts the hostname from the Origin URL automatically.</p>
<p>Here&rsquo;s why it matters: When Bunny fetches content from the origin server, it needs to send the right <code>Host</code> header in the HTTP request. If this is wrong or missing, Fathom&rsquo;s servers might not respond correctly. Setting the Origin Host Header ensures Bunny sends <code>Host: cdn.usefathom.com</code> when fetching the script.</p>
<p>You don&rsquo;t need special caching rules or edge rules. Default Pull Zone settings work fine.</p>
<h3 id="2-add-a-custom-hostname">2. Add a Custom Hostname</h3>
<p>In your Pull Zone settings, add a custom hostname. This is the subdomain you&rsquo;ll use—something like <code>ana.mydomain.com</code> or <code>cdn.mydomain.com</code>.</p>
<p>Choose something that makes sense for your setup. The subdomain name can be anything you want.</p>
<h3 id="3-configure-dns">3. Configure DNS</h3>
<p>Add a CNAME record in your DNS settings:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Type: CNAME
</span></span><span class="line"><span class="cl">Name: ana
</span></span><span class="line"><span class="cl">Value: ana-mydomain.b-cdn.net
</span></span></code></pre></td></tr></table>
</div>
</div><p>The value follows the pattern <code>&lt;pull-zone-name&gt;.b-cdn.net</code>, where <code>ana-mydomain</code> is the name you gave your Pull Zone in step 1. You&rsquo;ll also find this hostname in the Pull Zone settings after adding your custom hostname.</p>
<p>The subdomain name (<code>ana</code>) can be whatever you chose in the previous step—<code>cdn</code>, <code>track</code>, etc.</p>
<h3 id="4-enable-ssl">4. Enable SSL</h3>
<p>Bunny automatically provisions a free Let&rsquo;s Encrypt certificate once you add the custom hostname. This usually takes a few minutes to activate.</p>
<p>Check the SSL status in your Pull Zone settings. Once it shows &ldquo;Active,&rdquo; you&rsquo;re good to go.</p>
<p>I recommend enabling &ldquo;Force SSL&rdquo; to redirect all HTTP requests to HTTPS. This is just good practice for any modern website.</p>
<h3 id="5-update-your-script-tag">5. Update Your Script Tag</h3>
<p>Replace your existing Fathom script tag:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- Old: blocked by ad-blockers --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://cdn.usefathom.com/script.js&#34;</span> <span class="na">data-site</span><span class="o">=</span><span class="s">&#34;YOUR-SITE-ID&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>With your proxied version:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- New: served from your domain --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://ana.mydomain.com/script.js&#34;</span> <span class="na">data-site</span><span class="o">=</span><span class="s">&#34;YOUR-SITE-ID&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>That&rsquo;s it. No changes needed in your Fathom dashboard. The <code>data-site</code> attribute still identifies your site. Fathom doesn&rsquo;t care where the script loads from—the tracking calls use relative paths, so they go through the same proxy automatically.</p>
<h2 id="why-this-works">Why This Works</h2>
<p>Ad-blockers maintain blocklists of known tracking domains. These lists include <code>cdn.usefathom.com</code>, along with many other domains associated with analytics, advertising, and tracking.</p>
<p>When you serve the script from <code>ana.mydomain.com</code>, the blocker checks its list and finds nothing. Your domain isn&rsquo;t on any blocklist. The request goes through.</p>
<p><strong>This isn&rsquo;t about sneaking past privacy protections.</strong> Fathom is still privacy-first analytics that doesn&rsquo;t collect personal data. You&rsquo;re just ensuring that your own first-party analytics actually work.</p>
<p>It&rsquo;s the same principle as self-hosting the script on your own server—but using a CDN gives you better performance and easier updates.</p>
<h2 id="automating-the-entire-setup">Automating the Entire Setup</h2>
<p>Clicking through Bunny.net&rsquo;s dashboard to set up Pull Zones, DNS records, custom hostnames, and SSL certificates gets old fast when you&rsquo;re managing multiple sites. I needed a better way.</p>
<p>I ended up building a tool that handles this entire process programmatically: <a href="https://github.com/mrpesho/bunny-dns-sync">bunny-dns-sync</a>.</p>
<p>It&rsquo;s an open-source Python tool that lets you define your Bunny.net DNS zones, Pull Zones, custom hostnames, and Edge Rules in a JSON configuration file. Use it from the command line or import it as a library in your own scripts. The tool syncs your desired state to Bunny&rsquo;s API automatically — creating, updating, or deleting resources as needed.</p>
<p>Here&rsquo;s what a config file looks like:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;domains&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;mydomain.com&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;dns_records&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;CNAME&#34;</span><span class="p">,</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;ana&#34;</span><span class="p">,</span> <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;ana-mydomain.b-cdn.net&#34;</span><span class="p">,</span> <span class="nt">&#34;ttl&#34;</span><span class="p">:</span> <span class="mi">3600</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;pull_zones&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;ana-mydomain&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;origin_url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://cdn.usefathom.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;origin_host_header&#34;</span><span class="p">:</span> <span class="s2">&#34;cdn.usefathom.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;standard&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;enabled_regions&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;EU&#34;</span><span class="p">,</span> <span class="s2">&#34;US&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;hostnames&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;ana.mydomain.com&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;edge_rules&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Run the sync command, and it creates everything: DNS zones, Pull Zones, custom hostnames, and free SSL certificates. No manual clicking. Everything is version-controlled and reproducible.</p>
<p>This is completely optional — the manual setup works fine if you only have one or two sites. But if you&rsquo;re managing infrastructure as code or running multiple sites, the tool saves a lot of time and prevents configuration drift.</p>
<p>Check out the <a href="https://github.com/mrpesho/bunny-dns-sync">https://github.com/mrpesho/bunny-dns-sync</a> for installation instructions and full documentation.</p>
<h2 id="results-and-performance">Results and Performance</h2>
<p>This approach bypasses all domain-based blockers:</p>
<ul>
<li>uBlock Origin</li>
<li>AdGuard</li>
<li>Pi-hole</li>
<li>NextDNS</li>
<li>Any blocker using EasyList or similar lists</li>
</ul>
<p>Performance should be the same or slightly better than loading directly from Fathom&rsquo;s CDN. Bunny.net has edge nodes globally, so the script loads from whichever server is closest to your visitor.</p>
<p>For a ~2KB gzipped script, latency differences are negligible. I haven&rsquo;t noticed any performance degradation, and my analytics data is now complete.</p>
<h2 id="what-about-costs">What About Costs?</h2>
<p><a href="https://bunny.net?ref=cklqznj7qp">Bunny.net</a> charges by bandwidth. For an analytics script that&rsquo;s about 2KB gzipped, the cost is extremely low.</p>
<p>A site with 50,000 page views per month uses roughly 100MB of bandwidth for the script. At Bunny&rsquo;s pricing, that&rsquo;s well under $0.01/month for the actual bandwidth.</p>
<p>Bunny does have a minimum charge of $1/month if you use their services. But here&rsquo;s the thing: this minimum is hard to reach even if you have multiple sites running analytics through Bunny. The bandwidth usage is just too low.</p>
<p>This pairs well with <a href="https://usefathom.com/ref/VOSDJB">Fathom</a>, which allows up to 50 sites per account. You can proxy analytics for all your sites through Bunny and still barely touch that $1/month minimum.</p>
<h2 id="could-this-work-for-other-analytics-tools">Could This Work for Other Analytics Tools?</h2>
<p>I&rsquo;ve only tested this setup with Fathom and Bunny.net, so I can confirm it works reliably for this combination.</p>
<p>The principle should work for other privacy-focused analytics tools like Plausible or Umami—they face the same ad-blocker issues and use similar script-based tracking. The setup would be identical: point the Pull Zone at their CDN, configure your custom hostname, update your script tag.</p>
<h2 id="the-key-takeaway">The Key Takeaway</h2>
<p>Ad-blockers block by domain, not by functionality. Serving Fathom Analytics script from your own domain bypasses blocklists while preserving visitor privacy.</p>
<p>Using a CDN as a reverse proxy is cleaner than self-hosting (the script automatically updates when Fathom releases new versions) and more reliable than DNS-level URL forwarding (which didn&rsquo;t handle URL paths correctly with my DNS provider). The proxy routes both the script file and all tracking API calls through your domain, so ad-blockers never see requests to cdn.usefathom.com.</p>
<p>The setup takes about 10 minutes. The cost is about $1/month even with multiple sites. And you get complete analytics data from all your visitors—not just the 58% who don&rsquo;t use blockers.</p>
<p>If you&rsquo;re losing analytics data to ad-blockers, try this approach. Your dashboards will thank you.</p>
<hr>
<h2 id="disclaimers">Disclaimers</h2>
<p><strong>Privacy and Ethical Use</strong>: This technique is for privacy-respecting, first-party analytics (like Fathom, which is cookieless and doesn&rsquo;t collect personal data). Users have the right to control their browsing experience—bypassing ad-blockers doesn&rsquo;t exempt you from privacy laws or consent requirements where applicable. Be transparent: disclose analytics usage in your privacy policy and consider adding a footer notice explaining what data you collect and why.</p>
<p><strong>Affiliate Links</strong>: This post contains affiliate links to Fathom Analytics and Bunny.net. If you sign up through these links, I may receive a commission at no additional cost to you.</p>
<p><strong>Legal Disclaimer</strong>: I&rsquo;m not a lawyer, and this article does not constitute legal advice. This describes a technical solution I use on my own sites. Consult your own legal counsel about compliance with privacy regulations (GDPR, CCPA, etc.) applicable in your jurisdiction.</p>
]]></content:encoded><category>python</category><dc:creator>Aleš Kancilija</dc:creator></item><item><title>How I Cut Millions of Useless Backtests by Filtering Combinations Before They Run</title><link>https://aleskancilija.com/posts/optimize-backtesting-combinations/</link><pubDate>Sun, 08 Feb 2026 00:00:00 +0000</pubDate><guid>https://aleskancilija.com/posts/optimize-backtesting-combinations/</guid><description>Learn how to iterate through itertools.product() in memory-efficient batches and filter invalid combinations before they waste compute time.</description><content:encoded><![CDATA[<p>Three ranges. Nine values each. That&rsquo;s 729 combinations. Sounds manageable, right? Now add two more indicator parameters. Suddenly you&rsquo;re staring at 56 million combinations, your RAM is gone, and your backtest hasn&rsquo;t even started.</p>
<p>I hit this wall while backtesting forex indicators. I was testing different parameter combinations for indicators like MACD — things like fast EMA period, slow EMA period, and signal line length. Each parameter had a range of possible values, and I needed to test every valid combination.</p>
<p>The problem? Most combinations were invalid. MACD requires <code>signalLine &lt; fastEMA &lt; slowEMA</code>. But Python&rsquo;s <code>itertools.product</code> doesn&rsquo;t care about that. It generates <em>every</em> combination, including the ones that make no mathematical sense. I was wasting time and memory on millions of combinations I&rsquo;d immediately throw away.</p>
<p>Here&rsquo;s how I fixed it: a Python generator that filters invalid combinations <em>before</em> they pile up in memory, and processes the valid ones in small batches. The result? Fewer combinations, less memory, and backtests that actually finish.</p>
<h2 id="the-cartesian-product-problem">The Cartesian Product Problem</h2>
<p>When you test indicator parameters, you define ranges for each one. For MACD, that might look like:</p>
<ul>
<li><strong>Fast EMA</strong>: 3 to 21</li>
<li><strong>Slow EMA</strong>: 10 to 34</li>
<li><strong>Signal Line</strong>: 1 to 16</li>
</ul>
<p>To find the best settings, you need to test every valid combination. Python&rsquo;s <code>itertools.product</code> gives you the cartesian product — every possible pairing across all ranges.</p>
<p>Here&rsquo;s the basic approach using <code>zip(*product(...))</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">range_1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">range_2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">combo_1</span><span class="p">,</span> <span class="n">combo_2</span> <span class="o">=</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">product</span><span class="p">(</span><span class="n">range_1</span><span class="p">,</span> <span class="n">range_2</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;combo_1:&#34;</span><span class="p">,</span> <span class="n">combo_1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;combo_2:&#34;</span><span class="p">,</span> <span class="n">combo_2</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The <code>product()</code> function produces the cartesian product — every possible pair from the two ranges. Then <code>zip()</code> regroups those pairs into separate tuples, one per parameter. The output:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">combo_1: (1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4)
</span></span><span class="line"><span class="cl">combo_2: (1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4)
</span></span></code></pre></td></tr></table>
</div>
</div><p>Each column is a unique combination. Four values in each range gives 16 combinations. That&rsquo;s fine.</p>
<p>But what happens with real-world parameter ranges?</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">range_1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>    <span class="c1"># 49 values</span>
</span></span><span class="line"><span class="cl"><span class="n">range_2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>    <span class="c1"># 49 values</span>
</span></span><span class="line"><span class="cl"><span class="n">range_3</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>    <span class="c1"># 28 values</span>
</span></span><span class="line"><span class="cl"><span class="n">range_4</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mf">0.1</span><span class="p">)</span>           <span class="c1"># 299 values</span>
</span></span><span class="line"><span class="cl"><span class="n">range_5</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mf">0.1</span><span class="p">)</span>           <span class="c1"># 299 values</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># This creates 5 lists, each with 56,538,748 elements</span>
</span></span><span class="line"><span class="cl"><span class="n">combo_1</span><span class="p">,</span> <span class="n">combo_2</span><span class="p">,</span> <span class="n">combo_3</span><span class="p">,</span> <span class="n">combo_4</span><span class="p">,</span> <span class="n">combo_5</span> <span class="o">=</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">product</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">range_1</span><span class="p">,</span> <span class="n">range_2</span><span class="p">,</span> <span class="n">range_3</span><span class="p">,</span> <span class="n">range_4</span><span class="p">,</span> <span class="n">range_5</span>
</span></span><span class="line"><span class="cl"><span class="p">))</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Over 56 million elements — <em>per list</em>. Your machine will choke on memory before the backtest even begins. And that&rsquo;s regardless of which backtesting library you use. The bottleneck isn&rsquo;t the library. It&rsquo;s the sheer number of combinations.</p>
<h2 id="step-1-process-combinations-in-batches">Step 1: Process Combinations in Batches</h2>
<p>Both <code>product()</code> and <code>zip()</code> are iterators. They generate values one at a time, on demand. The memory problem happens because <code>zip(*product(...))</code> forces everything into memory at once.</p>
<p>The fix is a generator that collects combinations into small batches and yields them one group at a time:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">zip_product_in_batches</span><span class="p">(</span><span class="o">*</span><span class="n">iterables</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">1000</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Yields batches of zip(*product(...)) results,
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#39;size&#39; combinations at a time.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">size</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">int</span> <span class="ow">or</span> <span class="n">size</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="sa">f</span><span class="s2">&#34;&#39;size&#39; must be a positive integer. Got: </span><span class="si">{</span><span class="n">size</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">elements</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="o">*</span><span class="n">iterables</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">batch</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">elements</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">batch</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">size</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">yield</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">batch</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">batch</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Don&#39;t forget the last (possibly smaller) batch</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">batch</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">yield</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">batch</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Instead of materializing all 56 million combinations, you process them 1,000 at a time (or whatever batch size fits your machine). Here it is in action:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">range_1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">range_2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">combo_1</span><span class="p">,</span> <span class="n">combo_2</span> <span class="ow">in</span> <span class="n">zip_product_in_batches</span><span class="p">(</span><span class="n">range_1</span><span class="p">,</span> <span class="n">range_2</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">5</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;combo_1:&#34;</span><span class="p">,</span> <span class="n">combo_1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;combo_2:&#34;</span><span class="p">,</span> <span class="n">combo_2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">30</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Output:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">combo_1: (1, 1, 1, 1, 2)
</span></span><span class="line"><span class="cl">combo_2: (1, 2, 3, 4, 1)
</span></span><span class="line"><span class="cl">==============================
</span></span><span class="line"><span class="cl">combo_1: (2, 2, 2, 3, 3)
</span></span><span class="line"><span class="cl">combo_2: (2, 3, 4, 1, 2)
</span></span><span class="line"><span class="cl">==============================
</span></span><span class="line"><span class="cl">combo_1: (3, 3, 4, 4, 4)
</span></span><span class="line"><span class="cl">combo_2: (3, 4, 1, 2, 3)
</span></span><span class="line"><span class="cl">==============================
</span></span><span class="line"><span class="cl">combo_1: (4,)
</span></span><span class="line"><span class="cl">combo_2: (4,)
</span></span><span class="line"><span class="cl">==============================
</span></span></code></pre></td></tr></table>
</div>
</div><p>Same 16 combinations, but now they arrive in groups of 5. You process each batch, save the results, and move on. Memory stays flat no matter how many total combinations exist.</p>
<p>This alone makes it possible to run massive parameter sweeps on a regular laptop. But we can do much better.</p>
<h2 id="step-2-filter-out-invalid-combinations">Step 2: Filter Out Invalid Combinations</h2>
<p>Batching solves the memory problem. But you&rsquo;re still iterating over every single combination — including the invalid ones. For MACD, where <code>signalLine &lt; fastEMA &lt; slowEMA</code> must hold, most combinations from a broad parameter sweep are useless.</p>
<p>Take three ranges of 1–9 (like the signal line, fast EMA, and slow EMA). That&rsquo;s 9 × 9 × 9 = 729 total combinations. But how many actually satisfy the ascending order constraint? Only 84. That means <strong>88% of the work is wasted</strong> on combinations you&rsquo;ll never use.</p>
<p>The solution: filter inside the generator, before combinations ever reach the batch.</p>
<p>There are two ways to do this, and which one you pick depends on your filtering rule.</p>
<h3 id="when-the-same-operator-applies-to-every-pair">When the Same Operator Applies to Every Pair</h3>
<p>If every adjacent pair follows the same rule — like strict ascending order (<code>el_1 &lt; el_2 &lt; el_3 &lt; ...</code>) — you can generalize it. Python&rsquo;s built-in <code>all()</code> function checks whether every item in an iterable is true. That lets you apply one operator across any number of elements, so the function works with two or more parameter lists:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">filtered_zip_product_in_batches</span><span class="p">(</span><span class="o">*</span><span class="n">iterables</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">1000</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Yields batched zip(*product(...)) results, keeping only
</span></span></span><span class="line"><span class="cl"><span class="s2">    combinations where each element is strictly less than the next.
</span></span></span><span class="line"><span class="cl"><span class="s2">    Works with two or more iterables.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">size</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">int</span> <span class="ow">or</span> <span class="n">size</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="sa">f</span><span class="s2">&#34;&#39;size&#39; must be a positive integer. Got: </span><span class="si">{</span><span class="n">size</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="n">num_iterables</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">iterables</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">elements</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="o">*</span><span class="n">iterables</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Check: elements[0] &lt; elements[1] &lt; elements[2] &lt; ...</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">all</span><span class="p">(</span><span class="n">elements</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">elements</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_iterables</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">            <span class="n">batch</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">elements</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">batch</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">size</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">yield</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">batch</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">batch</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">batch</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">yield</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">batch</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The key line is the <code>all()</code> check. It walks through the elements pairwise: is the first less than the second? Is the second less than the third? And so on. Only if <em>every</em> pair passes does the combination make it into the batch.</p>
<p>This works great when the same operator applies everywhere. But what if it doesn&rsquo;t?</p>
<h3 id="when-operators-differ-between-pairs">When Operators Differ Between Pairs</h3>
<p>Sometimes the relationship between parameters isn&rsquo;t the same across the board. Maybe the first must be strictly less than the second, but the second can be less than <em>or equal to</em> the third — like <code>el_1 &lt; el_2 &lt;= el_3</code>. You can&rsquo;t express mixed operators in a single <code>all()</code> call, so spell out the parameters explicitly and write the condition by hand:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">filtered_zip_product_fixed</span><span class="p">(</span><span class="n">list_1</span><span class="p">,</span> <span class="n">list_2</span><span class="p">,</span> <span class="n">list_3</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">1000</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Batched zip-product for exactly 3 lists,
</span></span></span><span class="line"><span class="cl"><span class="s2">    keeping only combinations where list_1 &lt; list_2 &lt;= list_3.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">el_1</span><span class="p">,</span> <span class="n">el_2</span><span class="p">,</span> <span class="n">el_3</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">list_1</span><span class="p">,</span> <span class="n">list_2</span><span class="p">,</span> <span class="n">list_3</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Skip invalid combinations immediately</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">el_1</span> <span class="o">&lt;</span> <span class="n">el_2</span> <span class="o">&lt;=</span> <span class="n">el_3</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="n">batch</span><span class="o">.</span><span class="n">append</span><span class="p">([</span><span class="n">el_1</span><span class="p">,</span> <span class="n">el_2</span><span class="p">,</span> <span class="n">el_3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">batch</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">size</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">yield</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">batch</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">batch</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">batch</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">yield</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">batch</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>This gives you full control over each comparison. The downside is that the function is locked to a fixed number of parameter lists.</p>
<h2 id="seeing-it-in-action">Seeing It in Action</h2>
<p>Let&rsquo;s test with three ranges of 1–9 and a batch size of 10:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">range_1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">range_2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">range_3</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">list_1</span><span class="p">,</span> <span class="n">list_2</span><span class="p">,</span> <span class="n">list_3</span> <span class="ow">in</span> <span class="n">filtered_zip_product_in_batches</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">range_1</span><span class="p">,</span> <span class="n">range_2</span><span class="p">,</span> <span class="n">range_3</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;list_1:&#34;</span><span class="p">,</span> <span class="n">list_1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;list_2:&#34;</span><span class="p">,</span> <span class="n">list_2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;list_3:&#34;</span><span class="p">,</span> <span class="n">list_3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">25</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The first two batches look like this:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">list_1: (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
</span></span><span class="line"><span class="cl">list_2: (2, 2, 2, 2, 2, 2, 2, 3, 3, 3)
</span></span><span class="line"><span class="cl">list_3: (3, 4, 5, 6, 7, 8, 9, 4, 5, 6)
</span></span><span class="line"><span class="cl">=========================
</span></span><span class="line"><span class="cl">list_1: (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
</span></span><span class="line"><span class="cl">list_2: (3, 3, 3, 4, 4, 4, 4, 4, 5, 5)
</span></span><span class="line"><span class="cl">list_3: (7, 8, 9, 5, 6, 7, 8, 9, 6, 7)
</span></span><span class="line"><span class="cl">=========================
</span></span></code></pre></td></tr></table>
</div>
</div><p>Every single combination satisfies <code>list_1 &lt; list_2 &lt; list_3</code>. No wasted rows.</p>
<p>The unfiltered <code>zip_product_in_batches()</code> would have yielded all 729 combinations. The filtered version yields just 84. That&rsquo;s <strong>88.5% fewer combinations</strong> — fewer iterations, less compute time, less memory.</p>
<p>For a MACD backtest with realistic parameter ranges, the savings are even more dramatic. Instead of burning hours on combinations where the fast EMA is larger than the slow EMA (which makes no sense), you skip them entirely.</p>
<h2 id="adapting-the-filter-for-your-use-case">Adapting the Filter for Your Use Case</h2>
<p>The ascending-order filter (<code>a &lt; b &lt; c</code>) maps directly to MACD&rsquo;s constraint where <code>signalLine &lt; fastEMA &lt; slowEMA</code>. But you can swap in any filtering logic.</p>
<p>For example, if you need a minimum gap between parameters:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Keep only combinations where each value is at least 3 apart</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nb">all</span><span class="p">(</span><span class="n">elements</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">elements</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="mi">3</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_iterables</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)):</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Or if you have a completely custom rule — say, the third parameter must be less than half the second:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">elements</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">elements</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">/</span> <span class="mi">2</span><span class="p">:</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The pattern stays the same. Define your validity rule, check it before appending to the batch, and let the generator handle the rest.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Two small changes to how you generate parameter combinations can save you massive amounts of time and memory:</p>
<ol>
<li><strong>Batch the output</strong> so you never hold all combinations in memory at once.</li>
<li><strong>Filter inside the generator</strong> so invalid combinations never reach your backtest.</li>
</ol>
<p>For my forex indicator backtests, this turned an impossible parameter sweep into something I could run on a normal machine. The 729-to-84 reduction in the example above is modest — with real parameter ranges across multiple indicators, the invalid combinations easily outnumber the valid ones by 10:1 or more. How much you save will depend on your specific input ranges and filtering rules. Narrower ranges with strict constraints will cut more; wider ranges with loose rules will cut less.</p>
<p>The code is pure Python with no dependencies beyond <code>itertools</code>. Copy the <code>filtered_zip_product_in_batches</code> function, plug in your own filtering rule, and you&rsquo;re set.</p>
<hr>
<p><em><strong>Disclaimer:</strong> This article is for educational and informational purposes only. It does not constitute financial, investment, or trading advice. The examples involving forex indicators are used purely to illustrate a programming technique. Any trading or investment decisions you make are solely your own responsibility. Always do your own research and consult a qualified financial advisor before making trading decisions.</em></p>
]]></content:encoded><category>python</category><dc:creator>Aleš Kancilija</dc:creator></item><item><title>Reflex: Show rx.toast() Only on Small Screens</title><link>https://aleskancilija.com/posts/reflex-show-rx-toast-only-on-small-screens/</link><pubDate>Tue, 19 Aug 2025 00:00:00 +0000</pubDate><guid>https://aleskancilija.com/posts/reflex-show-rx-toast-only-on-small-screens/</guid><description>Learn how to conditionally display rx.toast() notifications only on mobile and tablet screens in Reflex using call_script and matchMedia.</description><content:encoded><![CDATA[<p><a href="https://reflex.dev">Reflex</a> makes it easy to conditionally render components by screen size using helpers like:</p>
<ul>
<li><code>rx.desktop_only()</code></li>
<li><code>rx.tablet_only()</code></li>
<li><code>rx.mobile_only()</code></li>
<li><code>rx.mobile_and_tablet()</code></li>
<li><code>rx.tablet_and_desktop()</code></li>
</ul>
<p>Example:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Tested with Reflex 0.8.5</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">reflex</span> <span class="k">as</span> <span class="nn">rx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Works as expected: renders only on mobile &amp; tablet widths</span>
</span></span><span class="line"><span class="cl"><span class="n">rx</span><span class="o">.</span><span class="n">mobile_and_tablet</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">rx</span><span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="s2">&#34;Shows on mobile and tablet, but not on desktop&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>These wrappers toggle visibility with CSS (<code>display: none</code> vs <code>block</code>) but children still mount.</p>
<h2 id="rxtoast-is-not-a-renderable-component">rx.toast() is not a renderable component</h2>
<p>This fails:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">reflex</span> <span class="k">as</span> <span class="nn">rx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ❌ Fails at compile time</span>
</span></span><span class="line"><span class="cl"><span class="n">rx</span><span class="o">.</span><span class="n">mobile_and_tablet</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">rx</span><span class="o">.</span><span class="n">toast</span><span class="p">(</span><span class="s2">&#34;Shows on mobile and tablet, but not on desktop&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The reason is that <code>rx.toast()</code> is an event action (an EventSpec), not a renderable component. Passing it as a child triggers a ChildrenTypeError:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">Component</span> <span class="n">Box</span> <span class="n">received</span> <span class="n">child</span> <span class="n">EventSpec</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">Accepted</span> <span class="n">types</span> <span class="n">are</span> <span class="n">components</span><span class="p">,</span> <span class="n">state</span> <span class="n">vars</span><span class="p">,</span> <span class="ow">or</span>
</span></span><span class="line"><span class="cl"><span class="n">primitive</span> <span class="n">Python</span> <span class="n">types</span><span class="o">.</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Even if you tried to call a toast in <code>on_mount</code> inside a responsive wrapper:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">reflex</span> <span class="k">as</span> <span class="nn">rx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ❌ Show on all screen sizes</span>
</span></span><span class="line"><span class="cl"><span class="n">rx</span><span class="o">.</span><span class="n">mobile_and_tablet</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">rx</span><span class="o">.</span><span class="n">el</span><span class="o">.</span><span class="n">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">on_mount</span><span class="o">=</span><span class="n">rx</span><span class="o">.</span><span class="n">toast</span><span class="p">(</span><span class="s2">&#34;Shows on mobile and tablet, but not on desktop&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>it would still show on all sizes because the child mounts regardless of visibility (the wrapper hides via CSS only).</p>
<h2 id="solution-call_script--state-callback">Solution: call_script → State callback</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">reflex</span> <span class="k">as</span> <span class="nn">rx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UiEvents</span><span class="p">(</span><span class="n">rx</span><span class="o">.</span><span class="n">State</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@rx.event</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">toast_if_small</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">is_small</span><span class="p">:</span> <span class="nb">bool</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">is_small</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">rx</span><span class="o">.</span><span class="n">toast</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Shows on mobile and tablet, but not on desktop&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">duration</span><span class="o">=</span><span class="mi">5000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">rx</span><span class="o">.</span><span class="n">page</span><span class="p">(</span><span class="n">route</span><span class="o">=</span><span class="s2">&#34;/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">rx</span><span class="o">.</span><span class="n">el</span><span class="o">.</span><span class="n">div</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">on_mount</span><span class="o">=</span><span class="n">rx</span><span class="o">.</span><span class="n">call_script</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Use matchMedia; it aligns better with CSS breakpoints than outerWidth </span>
</span></span><span class="line"><span class="cl">            <span class="sa">f</span><span class="s1">&#39;window.matchMedia(&#34;(max-width: 1024px)&#34;).matches&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">callback</span><span class="o">=</span><span class="n">UiEvents</span><span class="o">.</span><span class="n">toast_if_small</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="how-it-works">How it works</h3>
<ol>
<li>On mount, the client runs the JS code (<code>window.matchMedia('(max-width: 1024px)').matches</code>) which results in <code>true</code> if width is 1024px or less, otherwise <code>false</code>.</li>
<li>The boolean result is passed to <code>UiEvents.toast_if_small</code>.</li>
<li>The event function returns <code>rx.toast(...)</code> if the condition is met.</li>
</ol>
<h2 id="you-may-see-two-toasts-in-development">You may see two toasts in development</h2>
<p>Reflex uses React on the client. In dev mode, React&rsquo;s Strict Mode—enabled by Next.js during development—intentionally double-invokes mounts and effects, so you may see two toasts. Run <code>reflex run --env prod</code> to confirm the single-mount behavior in production.</p>
]]></content:encoded><category>python</category><dc:creator>Aleš Kancilija</dc:creator></item><item><title>Reflex: Change Configuration Based on Env Mode</title><link>https://aleskancilija.com/posts/reflex-change-configuration-based-on-env-mode/</link><pubDate>Tue, 25 Mar 2025 00:00:00 +0000</pubDate><guid>https://aleskancilija.com/posts/reflex-change-configuration-based-on-env-mode/</guid><description>Configure your Reflex Python app to use different .env files for development and production modes using REFLEX_ENV_MODE environment variable.</description><content:encoded><![CDATA[<p><a href="https://reflex.dev">Reflex</a> requires different configuration, when running in <strong>dev</strong> mode and when in <strong>prod</strong> mode.</p>
<p>Originally you have 2 options:</p>
<h2 id="set-env_file-in-terminal">Set ENV_FILE in terminal</h2>
<p>You can set ENV_FILE as a prefix to the command in the terminal:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># run command with env mode &#39;dev&#39;</span>
</span></span><span class="line"><span class="cl">$ <span class="nv">ENV_FILE</span><span class="o">=</span>./.env.dev reflex run
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># run command with env mode &#39;prod&#39;</span>
</span></span><span class="line"><span class="cl">$ <span class="nv">ENV_FILE</span><span class="o">=</span>./.env.prod reflex run --env prod  <span class="c1"># run it locally, or</span>
</span></span><span class="line"><span class="cl">$ <span class="nv">ENV_FILE</span><span class="o">=</span>./.env.prod reflex deploy  <span class="c1"># deploy it to the cloud</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="set-env_file-in-the-rxconfigpy">Set env_file in the rxconfig.py</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># rxconfig.py</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">reflex</span> <span class="k">as</span> <span class="nn">rx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="o">=</span> <span class="n">rx</span><span class="o">.</span><span class="n">Config</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">env_file</span><span class="o">=</span><span class="s2">&#34;.env.prod&#34;</span><span class="p">,</span>  <span class="c1"># or env_file=&#34;.env.dev&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>In this case you will have to change the value of <code>env_file</code> yourself, before you run it in different env mode.</p>
<h2 id="bonus-set-env_file-in-the-rxconfigpy-programmatically">Bonus: Set env_file in the rxconfig.py programmatically</h2>
<p>With a few lines of code, you can also make it switch automatically, based on the env mode.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">reflex</span> <span class="kn">import</span> <span class="n">constants</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">reflex</span> <span class="k">as</span> <span class="nn">rx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_value_based_on_env_mode</span><span class="p">(</span><span class="n">prod</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">dev</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Note that REFLEX_ENV_MODE variable is only set</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># with &#39;reflex run&#39;, but not with &#39;reflex deploy&#39;.</span>
</span></span><span class="line"><span class="cl">    <span class="n">env_mode</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;REFLEX_ENV_MODE&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">constants</span><span class="o">.</span><span class="n">Env</span><span class="o">.</span><span class="n">PROD</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">is_dev_mode</span> <span class="o">=</span> <span class="n">env_mode</span> <span class="o">==</span> <span class="n">constants</span><span class="o">.</span><span class="n">Env</span><span class="o">.</span><span class="n">DEV</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">dev</span> <span class="k">if</span> <span class="n">is_dev_mode</span> <span class="k">else</span> <span class="n">prod</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="o">=</span> <span class="n">rx</span><span class="o">.</span><span class="n">Config</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">env_file</span><span class="o">=</span><span class="n">get_value_based_on_env_mode</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">prod</span><span class="o">=</span><span class="s2">&#34;.env.prod&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">dev</span><span class="o">=</span><span class="s2">&#34;.env.dev&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded><category>python</category><dc:creator>Aleš Kancilija</dc:creator></item><item><title>Explore Python libs, vars, ...</title><link>https://aleskancilija.com/posts/explore-python-libs-vars/</link><pubDate>Mon, 26 Dec 2022 00:00:00 +0000</pubDate><guid>https://aleskancilija.com/posts/explore-python-libs-vars/</guid><description>Create custom Python functions to explore libraries, modules, and objects using dir() and docstrings. Filter results by keyword for faster development.</description><content:encoded><![CDATA[<h2 id="explore-by-name">Explore by name</h2>
<p>This function I often define in the ipython console, to symplify my work:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ddir</span><span class="p">(</span><span class="n">modul</span><span class="p">,</span> <span class="n">word</span><span class="o">=</span><span class="s1">&#39;&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">modul</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">word</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">in</span> <span class="n">d</span><span class="o">.</span><span class="n">lower</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>I name it simply <code>ddir()</code> with a double d.</p>
<p>The <code>dir()</code> function used in the 2nd row is a built-in Python function that takes our <code>modul</code> as parameter and returns the list of properties and functions as strings.</p>
<p>We can call our <code>ddir()</code> as</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">my_string</span> <span class="o">=</span> <span class="s1">&#39;Hey!&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ddir</span><span class="p">(</span><span class="n">my_string</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>and it will list all of the properties and functions of a string.</p>
<p>The way this function becomes useful to me is when I define the 2nd parameter as well:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">my_string</span> <span class="o">=</span> <span class="s1">&#39;Hey!&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ddir</span><span class="p">(</span><span class="n">my_string</span><span class="p">,</span> <span class="s1">&#39;is&#39;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>which, in this case, will list only properties and functions that include string &lsquo;is&rsquo;:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">isalnum
</span></span><span class="line"><span class="cl">isalpha
</span></span><span class="line"><span class="cl">isascii
</span></span><span class="line"><span class="cl">isdecimal
</span></span><span class="line"><span class="cl">isdigit
</span></span><span class="line"><span class="cl">isidentifier
</span></span><span class="line"><span class="cl">islower
</span></span><span class="line"><span class="cl">isnumeric
</span></span><span class="line"><span class="cl">isprintable
</span></span><span class="line"><span class="cl">isspace
</span></span><span class="line"><span class="cl">istitle
</span></span><span class="line"><span class="cl">isupper
</span></span></code></pre></td></tr></table>
</div>
</div><p>This works with all libraries, functions, classes, etc. and it makes it easier to develop the code.</p>
<h2 id="search-also-within-the-docstrings">Search also within the docStrings</h2>
<p>We can also go a step further and make it search inside docStrings as well:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">doc_dir</span><span class="p">(</span><span class="n">modul</span><span class="p">,</span> <span class="n">word</span><span class="o">=</span><span class="s1">&#39;&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">word_lc</span> <span class="o">=</span> <span class="n">word</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">modul</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">doc_string</span> <span class="o">=</span> <span class="n">modul</span><span class="o">.</span><span class="fm">__getattribute__</span><span class="p">(</span><span class="n">d</span><span class="p">)</span><span class="o">.</span><span class="vm">__doc__</span> <span class="ow">or</span> <span class="s1">&#39;&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">word_lc</span> <span class="ow">in</span> <span class="n">d</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">or</span> <span class="n">word_lc</span> <span class="ow">in</span> <span class="n">doc_string</span><span class="o">.</span><span class="n">lower</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;=&gt;&#39;</span><span class="p">,</span> <span class="n">d</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="n">doc_string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;---&#39;</span> <span class="o">*</span> <span class="mi">30</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Line 4 looks interesting. What we do here, is that we take a function name (i.e. &lsquo;zfill&rsquo;) that we get from <code>dir(modul)</code> as a string and we convert it into an actual function. Then we call its <code>__doc__</code> to get the docString. In some cases the docString is not defined and the <code>modul.__getattribute__(d).__doc__</code> will simply return None. To avoid getting</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">TypeError: argument of type &#39;NoneType&#39; is not iterable
</span></span></code></pre></td></tr></table>
</div>
</div><p>when searching for our <strong>word</strong>, we need to guarantee that a string is returned. Therefore in this code if 1st part returns <strong>None</strong>, the 2nd part makes sure that at least an empty string is returned.</p>
<p>Then in line 5, we check if our <strong>word</strong> is in the name or in the docString.</p>
<p>We can simply use it as:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">my_string</span> <span class="o">=</span> <span class="s1">&#39;key!&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">doc_dir</span><span class="p">(</span><span class="n">my_string</span><span class="p">,</span> <span class="s1">&#39;lower&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span> <span class="n">capitalize</span>
</span></span><span class="line"><span class="cl"><span class="n">Return</span> <span class="n">a</span> <span class="n">capitalized</span> <span class="n">version</span> <span class="n">of</span> <span class="n">the</span> <span class="n">string</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">More</span> <span class="n">specifically</span><span class="p">,</span> <span class="n">make</span> <span class="n">the</span> <span class="n">first</span> <span class="n">character</span> <span class="n">have</span> <span class="n">upper</span> <span class="k">case</span> <span class="ow">and</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">---------</span>
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span> <span class="n">islower</span>
</span></span><span class="line"><span class="cl"><span class="n">Return</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">the</span> <span class="n">string</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">lowercase</span> <span class="n">string</span><span class="p">,</span> <span class="kc">False</span> <span class="n">otherwise</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">A</span> <span class="n">string</span> <span class="ow">is</span> <span class="n">lowercase</span> <span class="k">if</span> <span class="nb">all</span> <span class="n">cased</span> <span class="n">characters</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">string</span> <span class="n">are</span>
</span></span><span class="line"><span class="cl"><span class="n">there</span> <span class="ow">is</span> <span class="n">at</span> <span class="n">least</span> <span class="n">one</span> <span class="n">cased</span> <span class="n">character</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">string</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">---------</span>
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span> <span class="n">istitle</span>
</span></span><span class="line"><span class="cl"><span class="n">Return</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">the</span> <span class="n">string</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">title</span><span class="o">-</span><span class="n">cased</span> <span class="n">string</span><span class="p">,</span> <span class="kc">False</span> <span class="n">otherwi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">In</span> <span class="n">a</span> <span class="n">title</span><span class="o">-</span><span class="n">cased</span> <span class="n">string</span><span class="p">,</span> <span class="n">upper</span><span class="o">-</span> <span class="ow">and</span> <span class="n">title</span><span class="o">-</span><span class="k">case</span> <span class="n">characters</span> <span class="n">may</span> <span class="n">on</span>
</span></span><span class="line"><span class="cl"><span class="n">follow</span> <span class="n">uncased</span> <span class="n">characters</span> <span class="ow">and</span> <span class="n">lowercase</span> <span class="n">characters</span> <span class="n">only</span> <span class="n">cased</span> <span class="n">on</span>
</span></span><span class="line"><span class="cl"><span class="o">---------</span>
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span> <span class="n">lower</span>
</span></span><span class="line"><span class="cl"><span class="n">Return</span> <span class="n">a</span> <span class="n">copy</span> <span class="n">of</span> <span class="n">the</span> <span class="n">string</span> <span class="n">converted</span> <span class="n">to</span> <span class="n">lowercase</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">---------</span>
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span> <span class="n">swapcase</span>
</span></span><span class="line"><span class="cl"><span class="n">Convert</span> <span class="n">uppercase</span> <span class="n">characters</span> <span class="n">to</span> <span class="n">lowercase</span> <span class="ow">and</span> <span class="n">lowercase</span> <span class="n">characte</span>
</span></span><span class="line"><span class="cl"><span class="o">---------</span>
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span> <span class="n">title</span>
</span></span><span class="line"><span class="cl"><span class="n">Return</span> <span class="n">a</span> <span class="n">version</span> <span class="n">of</span> <span class="n">the</span> <span class="n">string</span> <span class="n">where</span> <span class="n">each</span> <span class="n">word</span> <span class="ow">is</span> <span class="n">titlecased</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">More</span> <span class="n">specifically</span><span class="p">,</span> <span class="n">words</span> <span class="n">start</span> <span class="k">with</span> <span class="n">uppercased</span> <span class="n">characters</span> <span class="ow">and</span> <span class="n">al</span>
</span></span><span class="line"><span class="cl"><span class="n">cased</span> <span class="n">characters</span> <span class="n">have</span> <span class="n">lower</span> <span class="k">case</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">---------</span>
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded><category>python</category><dc:creator>Aleš Kancilija</dc:creator></item><item><title>List dependencies of the Python libraries in your project</title><link>https://aleskancilija.com/posts/list-dependencies-of-python-libraries/</link><pubDate>Mon, 14 Nov 2022 00:00:00 +0000</pubDate><guid>https://aleskancilija.com/posts/list-dependencies-of-python-libraries/</guid><description>Use johnnydep to list and analyze dependencies of Python packages in your project. Includes commands for checking installed and latest library versions.</description><content:encoded><![CDATA[<p>There&rsquo;s an interesting library available, that lists s. We&rsquo;ll cover just the basics here, but I encourage you to explore it in more more depth on your own.</p>
<h2 id="the-johnnydep-library">The johnnydep library</h2>
<p>You can simply install it with <code>pip install johnnydep</code>.</p>
<p>Then, if you want to get the list of dependencies of latest version of i.e Django library, you can call it with:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">johnnydep Django
</span></span></code></pre></td></tr></table>
</div>
</div><p>and it will output:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">2022-10-29 19:15:46 [info    ] init johnnydist           [johnnydep.lib]
</span></span><span class="line"><span class="cl">2022-10-29 19:15:50 [info    ] init johnnydist           [johnnydep.lib]
</span></span><span class="line"><span class="cl">2022-10-29 19:15:51 [info    ] init johnnydist           [johnnydep.lib]
</span></span><span class="line"><span class="cl">2022-10-29 19:15:52 [info    ] init johnnydist           [johnnydep.lib]
</span></span><span class="line"><span class="cl">name                 summary
</span></span><span class="line"><span class="cl">-------------------  ------------------------------------------------------
</span></span><span class="line"><span class="cl">Django               A high-level Python web framework that e
</span></span><span class="line"><span class="cl">├── asgiref&lt;4,&gt;=3.5.2  ASGI specs, helper code, and adapters
</span></span><span class="line"><span class="cl">├── backports.zoneinfo Backport of the standard library zoneinfo
</span></span><span class="line"><span class="cl">└── sqlparse&gt;=0.2.2    A non-validating SQL parser.
</span></span></code></pre></td></tr></table>
</div>
</div><p>Or you can specify the library&rsquo;s version as well:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">johnnydep <span class="nv">Django</span><span class="o">==</span>4.0.3
</span></span></code></pre></td></tr></table>
</div>
</div><p>and it will list the dependencies for that version:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">2022-10-29 19:16:55 [info    ] init johnnydist           [johnnydep.lib]
</span></span><span class="line"><span class="cl">2022-10-29 19:16:56 [info    ] init johnnydist           [johnnydep.lib]
</span></span><span class="line"><span class="cl">2022-10-29 19:16:57 [info    ] init johnnydist           [johnnydep.lib]
</span></span><span class="line"><span class="cl">2022-10-29 19:16:58 [info    ] init johnnydist           [johnnydep.lib]
</span></span><span class="line"><span class="cl">name                 summary
</span></span><span class="line"><span class="cl">-------------------  ------------------------------------------------------
</span></span><span class="line"><span class="cl">Django==4.0.3        A high-level Python web framework that e
</span></span><span class="line"><span class="cl">├── asgiref&lt;4,&gt;=3.4.1  ASGI specs, helper code, and adapters
</span></span><span class="line"><span class="cl">├── backports.zoneinfo Backport of the standard library zoneinfo
</span></span><span class="line"><span class="cl">└── sqlparse&gt;=0.2.2    A non-validating SQL parser.
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="list-dependencies-of-all-libraries-in-your-project">List dependencies of all libraries in your project</h2>
<p>Let&rsquo;s first activate the virtualenv or your project:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> your/project/folder/
</span></span><span class="line"><span class="cl"><span class="nb">source</span> &lt;venv_name&gt;/bin/activate
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="list-dependencies-of-installed-version-of-each-library">List dependencies of installed version of each library</h3>
<p>Ok. Now, if you want to list the libraries of your entire project, you can call:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pip freeze <span class="p">|</span> xargs -n1 johnnydep
</span></span></code></pre></td></tr></table>
</div>
</div><p>where <code>pip freeze</code> lists all installed libraries with their versions (1 library per line) and the <code>-n1</code> parameter of xargs command ensures that it will feed 1 line at a time to the <code>johnnydep</code> library. This will list the dependencies of all libraries in you project, for the versions that you use.</p>
<h3 id="list-dependencies-of-the-latest-version-of-each-library">List dependencies of the latest version of each library</h3>
<p>You can also list all the dependencies of the <strong>latest</strong> versions of all of the libraries from your project, by calling:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pip freeze <span class="p">|</span> awk -F <span class="s2">&#34;==&#34;</span> <span class="s1">&#39;{print $1}&#39;</span> <span class="p">|</span> xargs -n1 johnnydep
</span></span></code></pre></td></tr></table>
</div>
</div><p>The <code>awk -F &quot;==&quot; '{print $1}'</code> will split the e.g. <code>Django==4.0.3</code> into <code>Django</code> and <code>4.0.3</code> and will pass only the first value to the <code>johnnydep</code> library.</p>
]]></content:encoded><category>python</category><dc:creator>Aleš Kancilija</dc:creator></item><item><title>csv file to dict</title><link>https://aleskancilija.com/posts/csv-file-to-dict/</link><pubDate>Sun, 24 May 2020 00:00:00 +0000</pubDate><guid>https://aleskancilija.com/posts/csv-file-to-dict/</guid><description>Python function to read CSV files and convert rows to dictionaries with optional column filtering and type conversion for data processing.</description><content:encoded><![CDATA[<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">csv_to_dict</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">allowed_keys</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Reads csv file and returns dict with keys specified in allowed_keys
</span></span></span><span class="line"><span class="cl"><span class="s2">    parameter; if allowed_keys is Falsy, it will include all columns.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    path - string, path to file
</span></span></span><span class="line"><span class="cl"><span class="s2">    allowed_keys - array of strings, columns that you want in the result JSON
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    return - dict
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">rows_json</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s1">&#39;r&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">csv_file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">csv_content</span> <span class="o">=</span> <span class="n">csv_file</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">keys</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="p">:</span> <span class="n">k</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">csv_content</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;,&#39;</span><span class="p">))}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">allowed_keys</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">allowed_keys</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">keys</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">csv_content</span><span class="p">[</span><span class="mi">1</span><span class="p">:]:</span>
</span></span><span class="line"><span class="cl">            <span class="n">row</span> <span class="o">=</span> <span class="n">row</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;,&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">rows_json</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="n">keys</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span> <span class="p">(</span><span class="n">k</span> <span class="k">if</span> <span class="n">k</span> <span class="o">!=</span> <span class="s1">&#39;NaN&#39;</span> <span class="k">else</span> <span class="kc">None</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">row</span><span class="p">)</span> <span class="k">if</span> <span class="n">keys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="ow">in</span> <span class="n">allowed_keys</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="kn">import</span> <span class="nn">traceback</span>
</span></span><span class="line"><span class="cl">        <span class="n">traceback</span><span class="o">.</span><span class="n">print_exc</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">rows_json</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">convert_dict_values</span><span class="p">(</span><span class="n">array_of_dicts</span><span class="p">,</span> <span class="n">type_per_key</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Convert type of values in dictionary
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    - array_of_dicts, i.e. [{&#39;pressure&#39;: &#39;2.4&#39;, &#39;wheels&#39;: &#39;0&#39;, &#39;name&#39;: &#39;Audi&#39;}, ...]
</span></span></span><span class="line"><span class="cl"><span class="s2">    - type_per_key, i.e. {&#39;pressure&#39;: float, &#39;wheels&#39;: int}
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    return: [{&#39;pressure&#39;: 2.4, &#39;wheels&#39;: 0, &#39;name&#39;: &#39;Audi&#39;}, ...]
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">_dict</span> <span class="ow">in</span> <span class="n">array_of_dicts</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">_dict</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">i_dict</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">type_per_key</span><span class="p">[</span><span class="n">k</span><span class="p">](</span><span class="n">v</span><span class="p">)</span> <span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">type_per_key</span> <span class="k">else</span> <span class="n">v</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">array_of_dicts</span>
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded><category>python</category><dc:creator>Aleš Kancilija</dc:creator></item><item><title>Python: iteration speed test between list, tuple and set</title><link>https://aleskancilija.com/posts/python-iteration-speed-test-between-list-tuple-and-set/</link><pubDate>Sat, 25 Jan 2020 00:00:00 +0000</pubDate><guid>https://aleskancilija.com/posts/python-iteration-speed-test-between-list-tuple-and-set/</guid><description>Benchmark comparison showing Python set is up to 11,000x faster than list or tuple for membership testing with large datasets.</description><content:encoded><![CDATA[<h2 id="lets-start-with-a-practical-example">Let&rsquo;s start with a practical example:</h2>
<p>We have a list of IDs that need to be processed. This list can hold from just a dozen of IDs to up to a few 100,000 IDs. Each ID is processed only once in each iteration and the order of IDs is not important.</p>
<p>This whole thing is presenting a bottleneck in our application, so we decided to take a look at other iterable data structures that Python offers.</p>
<h2 id="to-demonstrate-this-we-will-create-a-simple-example">To demonstrate this, we will create a simple example:</h2>
<ol>
<li>Function that generates a shuffled list of positive and negative numbers:</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">shuffle</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_numbers</span><span class="p">(</span><span class="n">power</span><span class="o">=</span><span class="mi">2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">low_end</span> <span class="o">=</span> <span class="o">-</span><span class="mi">10</span> <span class="o">**</span> <span class="n">power</span>
</span></span><span class="line"><span class="cl">    <span class="n">high_end</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">**</span> <span class="n">power</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># generate list</span>
</span></span><span class="line"><span class="cl">    <span class="n">arr</span> <span class="o">=</span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">low_end</span><span class="p">,</span> <span class="n">high_end</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># shuffle list</span>
</span></span><span class="line"><span class="cl">    <span class="n">shuffle</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># remove 25% of numbers, less numbers will have it&#39;s pair</span>
</span></span><span class="line"><span class="cl">    <span class="n">remove_n_elements</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span> <span class="o">*</span> <span class="mf">.25</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">arr</span> <span class="o">=</span> <span class="n">arr</span><span class="p">[</span><span class="n">remove_n_elements</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">arr</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>This will return:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">get_numbers</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>  <span class="c1"># list of 150 unique numbers, between -100 and 100</span>
</span></span><span class="line"><span class="cl"><span class="n">get_numbers</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>  <span class="c1"># list of 1500 unique numbers, between -1000 and 1000</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="2">
<li>Then we will define a simple function that will iterate through list, tuple or set and find pairs, where the sum of both numbers equals sum:</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">find_pairs</span><span class="p">(</span><span class="n">arr</span><span class="p">,</span> <span class="nb">sum</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">pairs</span> <span class="o">=</span> <span class="p">{}</span> 
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">arr</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nb">sum</span> <span class="o">-</span> <span class="n">i</span><span class="p">)</span> <span class="ow">in</span> <span class="n">arr</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">pairs</span><span class="o">.</span><span class="n">add</span><span class="p">((</span><span class="n">i</span><span class="p">,</span> <span class="nb">sum</span> <span class="o">-</span> <span class="n">i</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">pairs</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="3">
<li>And let&rsquo;s also create a function that will measure the execution time of find_pairs() function:</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">measure_time</span><span class="p">(</span><span class="n">nums</span><span class="p">,</span> <span class="nb">sum</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">pairs</span> <span class="o">=</span> <span class="n">find_pairs</span><span class="p">(</span><span class="n">nums</span><span class="p">,</span> <span class="nb">sum</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">end</span> <span class="o">-</span> <span class="n">start</span>  <span class="c1"># seconds</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Okay, now we can measure time, based on number of elements.</p>
<h2 id="150-numbers">150 numbers:</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">numbers</span> <span class="o">=</span> <span class="n">get_numbers</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>         <span class="c1"># list of 150 numbers</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="n">numbers</span><span class="p">,</span> <span class="mi">17</span><span class="p">)</span>        <span class="c1"># ~ 0.000271 seconds</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="nb">tuple</span><span class="p">(</span><span class="n">numbers</span><span class="p">),</span> <span class="mi">17</span><span class="p">)</span> <span class="c1"># ~ 0.000224 seconds</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">numbers</span><span class="p">),</span> <span class="mi">17</span><span class="p">)</span>   <span class="c1"># ~ 0.000046 seconds</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>With 150 numbers, using set is ~5.89 times faster than using list.</p>
<h2 id="1500-numbers">1,500 numbers:</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">numbers</span> <span class="o">=</span> <span class="n">get_numbers</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>         <span class="c1"># list of 1,500 numbers</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="n">numbers</span><span class="p">,</span> <span class="mi">17</span><span class="p">)</span>        <span class="c1"># ~ 0.025494 seconds</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="nb">tuple</span><span class="p">(</span><span class="n">numbers</span><span class="p">),</span> <span class="mi">17</span><span class="p">)</span> <span class="c1"># ~ 0.026308 seconds</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">numbers</span><span class="p">),</span> <span class="mi">17</span><span class="p">)</span>   <span class="c1"># ~ 0.000418 seconds</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>With 1,500 numbers, using set is ~60.99 times faster than using list.</p>
<h2 id="15000-numbers">15,000 numbers:</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">numbers</span> <span class="o">=</span> <span class="n">get_numbers</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>         <span class="c1"># list of 15,000 numbers</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="n">numbers</span><span class="p">,</span> <span class="mi">17</span><span class="p">)</span>        <span class="c1"># ~ 3.825252 seconds</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="nb">tuple</span><span class="p">(</span><span class="n">numbers</span><span class="p">),</span> <span class="mi">17</span><span class="p">)</span> <span class="c1"># ~ 3.786150 seconds</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">numbers</span><span class="p">),</span> <span class="mi">17</span><span class="p">)</span>   <span class="c1"># ~ 0.006523 seconds</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>With 15,000 numbers, using set is ~586.42 times faster than using list.</p>
<h2 id="150000-numbers">150,000 numbers:</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">numbers</span> <span class="o">=</span> <span class="n">get_numbers</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>         <span class="c1"># list of 150,000 numbers</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="n">numbers</span><span class="p">,</span> <span class="mi">17</span><span class="p">)</span>        <span class="c1"># ~ 834.580523 s = 13 min 54.58 sec</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="nb">tuple</span><span class="p">(</span><span class="n">numbers</span><span class="p">),</span> <span class="mi">17</span><span class="p">)</span> <span class="c1"># ~ 854.708630 s = 14 min 14.70 sec</span>
</span></span><span class="line"><span class="cl"><span class="n">measure_time</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">numbers</span><span class="p">),</span> <span class="mi">17</span><span class="p">)</span>   <span class="c1"># ~ 0.07081294 seconds</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>With 150,000 numbers, using set is ~11,785.70 times faster than using list.</p>
<h2 id="conclusion">Conclusion:</h2>
<p>list and tuple work pretty much with same speed, but set made a big difference, here.</p>
<p>By using the set instead of list we significantly sped up the code.</p>
]]></content:encoded><category>python</category><dc:creator>Aleš Kancilija</dc:creator></item></channel></rss>