<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Imports on josiete.com</title><link>https://www.josiete.com/en/tags/imports/</link><description>Recent content in Imports on josiete.com</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Tue, 20 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.josiete.com/en/tags/imports/index.xml" rel="self" type="application/rss+xml"/><item><title>Python 3.15: import only when needed</title><link>https://www.josiete.com/en/posts/python-explicit-lazy-imports/</link><pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate><guid>https://www.josiete.com/en/posts/python-explicit-lazy-imports/</guid><description>&lt;p&gt;&lt;img src="https://www.josiete.com/images/python-explicit-lazy-imports.webp" alt="Raised blue and yellow Python logo on a dark surface, with blurred blocks in the background"&gt;&lt;/p&gt;&#10;&lt;p&gt;Importing a library can involve reading files, executing initialization code, and loading further dependencies. We pay that cost even when we only want to display a tool&amp;rsquo;s help message. &lt;strong&gt;Python 3.15 introduces Explicit Lazy Imports&lt;/strong&gt;, defined in &lt;a href="https://peps.python.org/pep-0810/"&gt;PEP 810&lt;/a&gt; and covered in &lt;a href="https://docs.python.org/3.15/whatsnew/3.15.html#pep-810-explicit-lazy-imports"&gt;What&amp;rsquo;s New in Python 3.15&lt;/a&gt;: we can declare an import and defer loading until first use.&lt;/p&gt;&#10;&lt;h2 id="a-small-example"&gt;A small example&lt;/h2&gt;&#10;&lt;p&gt;This script requires Python 3.15 or later and uses only the standard library:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# tool.py&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; argparse&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;lazy &lt;span style="color:#f92672"&gt;import&lt;/span&gt; zoneinfo&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;parser &lt;span style="color:#f92672"&gt;=&lt;/span&gt; argparse&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ArgumentParser()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;parser&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_argument(&lt;span style="color:#e6db74"&gt;&amp;#34;--zone&amp;#34;&lt;/span&gt;, help&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Look up an IANA time zone&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;args &lt;span style="color:#f92672"&gt;=&lt;/span&gt; parser&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parse_args()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;zone:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(zoneinfo&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ZoneInfo(args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;zone)) &lt;span style="color:#75715e"&gt;# zoneinfo loads here&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;No time zone lookup requested&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With &lt;code&gt;python tool.py --help&lt;/code&gt;, the tool exits without loading &lt;code&gt;zoneinfo&lt;/code&gt;, provided another dependency has not already imported it. With &lt;code&gt;python tool.py --zone Europe/Madrid&lt;/code&gt;, accessing &lt;code&gt;zoneinfo&lt;/code&gt; triggers loading; this lookup requires system time zone data or the &lt;code&gt;tzdata&lt;/code&gt; package.&lt;/p&gt;&#10;&lt;p&gt;There is also &lt;code&gt;lazy from zoneinfo import ZoneInfo&lt;/code&gt;. Loading is deferred for the whole module; Python does not execute just the requested class. The &lt;a href="https://docs.python.org/3.15/reference/simple_stmts.html#lazy-imports"&gt;language reference&lt;/a&gt; restricts this syntax to module scope: it is not allowed inside functions, classes, or &lt;code&gt;try&lt;/code&gt; blocks, or with &lt;code&gt;import *&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="where-work-is-saved"&gt;Where work is saved&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Faster startup:&lt;/strong&gt; dependency initialization is postponed until an execution path needs it.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Less unnecessary CPU work and I/O:&lt;/strong&gt; a tool with a charting option could avoid loading a heavy visualization library when it only exports text.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Lower memory use:&lt;/strong&gt; modules that never load do not create their objects or initialize dependencies used exclusively by them.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Savings depend on the program. If we eventually use every module, much of the cost simply moves to a different moment. Compare fresh processes, measuring startup, the first operation, and memory under representative workloads; there is no universal improvement percentage.&lt;/p&gt;&#10;&lt;h2 id="the-costs-of-postponing-imports"&gt;The costs of postponing imports&lt;/h2&gt;&#10;&lt;p&gt;&lt;strong&gt;Slower first access:&lt;/strong&gt; the first operation needing the module pays for its import. A service might benefit from preloading it before accepting requests to avoid that initial latency.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Errors surface later:&lt;/strong&gt; a missing dependency or initialization failure may go unnoticed until the relevant feature runs. Test optional paths too, and catch errors around usage where appropriate. Import side effects, such as plugin registration, are also delayed.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Static typing needs compatible tools:&lt;/strong&gt; according to the PEP, an analyzer can resolve these names as it would an ordinary import; laziness does not make the type unknown. The new syntax does require support from the analyzer and formatter. Static analysis also differs from execution: a framework evaluating annotations may trigger module loading. &lt;code&gt;lazy import&lt;/code&gt; and imports guarded by &lt;code&gt;TYPE_CHECKING&lt;/code&gt; have different runtime behavior.&lt;/p&gt;&#10;&lt;p&gt;For compatibility with older versions, use &lt;code&gt;__lazy_modules__ = [&amp;quot;zoneinfo&amp;quot;]&lt;/code&gt; followed by &lt;code&gt;import zoneinfo&lt;/code&gt;: it becomes lazy from 3.15 onward and remains eager in earlier versions. The &lt;code&gt;lazy&lt;/code&gt; syntax raises a syntax error in Python 3.14 and earlier.&lt;/p&gt;&#10;</description></item></channel></rss>