<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<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>String Formatting on KbWen Blog</title>
    <link>https://www.kbwen.com/tags/string-formatting/</link>
    <description>KbWen 的個人技術部落格，分享 Python、機器學習、深度學習、資料工程與 AI 開發的學習筆記與實作心得。</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <image>
      <url>https://www.kbwen.com/images/og-default.png</url>
      <title>KbWen Blog</title>
      <link>https://www.kbwen.com/</link>
    </image>
    
    <lastBuildDate>Sun, 28 Jun 2026 08:00:00 +0800</lastBuildDate><atom:link href="https://www.kbwen.com/tags/string-formatting/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Python f-string：你可能只用到一半</title>
      <link>https://www.kbwen.com/python-f-string/</link>
      <pubDate>Sun, 28 Jun 2026 08:00:00 +0800</pubDate><dc:creator>KbWen</dc:creator>
      <guid>https://www.kbwen.com/python-f-string/</guid>
      <description>f-string 不只是 f&amp;#34;{變數}&amp;#34;。冒號後面的格式設定、3.8 的 = 自我說明、3.12（PEP 701）鬆綁的同引號巢狀與跨行，一層一層看完，順便聊什麼時候別用它。</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong>：大部分人用 f-string 停在 <code>f&quot;{name}&quot;</code> 就不往下了，但好用的東西都在後面。冒號後面是格式設定（<code>f&quot;{price:.2f}&quot;</code>、千分位 <code>:,</code>、對齊 <code>:&gt;10</code>）；<code>f&quot;{x=}&quot;</code> 會直接印出 <code>x=3</code>，debug 時超省事（Python 3.8 起）；Python 3.12 之後（<a href="https://peps.python.org/pep-0701/">PEP 701</a>）連同引號巢狀、跨行、反斜線都解禁了，以前會 <code>SyntaxError</code> 的現在能寫。記住一句「冒號前是值，冒號後是長相」，大概就摸到八成了。</p>
</blockquote>
<p><code>f&quot;{name}&quot;</code> 這個寫法大家應該都會。要把變數塞進字串，前面加個 <code>f</code>、變數用 <code>{}</code> 框起來：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">name</span> <span class="o">=</span> <span class="s2">&#34;Ada&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;hello, </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">## hello, Ada</span>
</span></span></code></pre></div><p>光這一步，就比以前的 <code>&quot;%s&quot; % name</code> 跟 <code>&quot;{}&quot;.format(name)</code> 好讀太多，變數一多差距更明顯，你不用再去數後面括號裡的參數順序對不對。所以這篇不打算花篇幅比那兩個舊寫法，<a href="https://peps.python.org/pep-0498/">PEP 498</a> 在 Python 3.6 就把這件事定下來了，能用 f-string 就用。</p>
<p>只是大多數人也就停在這裡。<code>{}</code> 裡面能放什麼、後面那串能寫什麼，才是 f-string 真正好用的地方。</p>
<h2 id="冒號後面那串">冒號後面那串</h2>
<p><code>{}</code> 裡可以放的不只是變數名，是任何運算式。算式、呼叫方法、取索引都行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">8</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>              <span class="c1">## 11</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>       <span class="c1">## ADA</span>
</span></span></code></pre></div><p>真正常被忽略的是冒號。<code>{}</code> 裡放一個冒號，前面是值，後面是它要長成什麼樣子：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">price</span> <span class="o">=</span> <span class="mi">1</span><span class="o">/</span><span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">price</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>          <span class="c1">## 0.33   取小數兩位</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="mi">1234567</span><span class="si">:</span><span class="s2">,</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>          <span class="c1">## 1,234,567   加千分位逗號</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="mf">0.1827</span><span class="si">:</span><span class="s2">.1%</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>         <span class="c1">## 18.3%   轉百分比</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="mi">255</span><span class="si">:</span><span class="s2">x</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>              <span class="c1">## ff    轉十六進位</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="mi">5</span><span class="si">:</span><span class="s2">b</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>                <span class="c1">## 101   轉二進位</span>
</span></span></code></pre></div><p>對齊也是同一個位置。<code>&gt;</code> 靠右、<code>&lt;</code> 靠左、<code>^</code> 置中，後面接寬度：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="si">:</span><span class="s2">&gt;10</span><span class="si">}</span><span class="s2">|&#34;</span><span class="p">)</span>          <span class="c1">## &#39;       Ada|&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="si">:</span><span class="s2">^10</span><span class="si">}</span><span class="s2">|&#34;</span><span class="p">)</span>          <span class="c1">## &#39;   Ada    |&#39;</span>
</span></span></code></pre></div><p>排東西成一欄的時候這個很順手，不用自己補空白。寬度還能是動態的——把另一個變數再用一層 <code>{}</code> 塞進去：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">w</span> <span class="o">=</span> <span class="mi">8</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="si">:</span><span class="s2">&gt;</span><span class="si">{</span><span class="n">w</span><span class="si">}}</span><span class="s2">|&#34;</span><span class="p">)</span>         <span class="c1">## &#39;     Ada|&#39;</span>
</span></span></code></pre></div><p>冒號後面那串叫 format spec，本身是一套小語法，上面只是最常用的幾個。記住「冒號前是值，冒號後是長相」，剩下要用再查就有。</p>
<h2 id="變數後面加個-debug-省一半">變數後面加個 <code>=</code>，debug 省一半</h2>
<p>這個我覺得是 f-string 最被低估的功能。你在 <code>{}</code> 裡的變數後面加一個 <code>=</code>，它會連名字帶值一起印出來：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">x</span><span class="si">=}</span><span class="s2">&#34;</span><span class="p">)</span>                 <span class="c1">## x=3</span>
</span></span></code></pre></div><p>聽起來沒什麼，但想想你平常 debug 怎麼印變數的。大概是 <code>print(&quot;x =&quot;, x)</code> 這樣打兩次 <code>x</code>，改名字還得改兩個地方。<code>f&quot;{x=}&quot;</code> 一次搞定，而且名字跟值保證對得起來。算式也行，它會把整串原樣印出來：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">x</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">+</span> <span class="mi">1</span><span class="si">=}</span><span class="s2">&#34;</span><span class="p">)</span>        <span class="c1">## x * 10 + 1=31</span>
</span></span></code></pre></div><p><code>=</code> 後面沒接東西時，值是用 <code>repr()</code> 印的，所以字串會自己帶引號，剛好分得出空字串跟一格空白。想要沒引號的 <code>str()</code> 版本，才要加 <code>!s</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">name</span> <span class="o">=</span> <span class="s2">&#34;Ada&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="si">=}</span><span class="s2">&#34;</span><span class="p">)</span>              <span class="c1">## name=&#39;Ada&#39;   預設用 repr, 帶引號</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="si">=!s}</span><span class="s2">&#34;</span><span class="p">)</span>            <span class="c1">## name=Ada     !s 改用 str, 沒引號</span>
</span></span></code></pre></div><p>這是 Python 3.8 加的。我自己現在 debug 幾乎只用這招，臨時想看某個值長怎樣，<code>f&quot;{那個值=}&quot;</code> 包一下就好。</p>
<h2 id="312-之後鬆綁的那些限制">3.12 之後鬆綁的那些限制</h2>
<p>f-string 早期有些很煩的限制，到 Python 3.12（<a href="https://peps.python.org/pep-0701/">PEP 701</a>）才一次解掉。最常踩到的是引號。3.12 以前，f-string 裡面不能再用同一種引號，所以這行會直接 <code>SyntaxError</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">d</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;key&#34;</span><span class="p">:</span> <span class="s2">&#34;val&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">d</span><span class="p">[</span><span class="s2">&#34;key&#34;</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span>                  <span class="c1">## 3.12 以前：SyntaxError</span>
</span></span></code></pre></div><p>以前的解法是裡外換引號（<code>f&quot;{d['key']}&quot;</code>），或者乾脆先把值拉出來。3.12 之後就沒這回事了，同引號照寫照跑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">d</span><span class="p">[</span><span class="s2">&#34;key&#34;</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>           <span class="c1">## val   （3.12+ 才行）</span>
</span></span></code></pre></div><p>反斜線也是。以前運算式裡塞不進反斜線，連 <code>'\n'.join(...)</code> 這種常見寫法都得繞道：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">xs</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="s2">&#34;b&#34;</span><span class="p">,</span> <span class="s2">&#34;c&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>      <span class="c1">## 3.12+ 才行，以前會炸</span>
</span></span><span class="line"><span class="cl"><span class="c1">## a</span>
</span></span><span class="line"><span class="cl"><span class="c1">## b</span>
</span></span><span class="line"><span class="cl"><span class="c1">## c</span>
</span></span></code></pre></div><p>還有跨行、<code>{}</code> 裡寫註解，現在也都合法了。要注意的就是版本——這些是 3.12 以後才有的，如果你的環境還卡在更舊的版本，上面那幾種寫法還是會炸，該繞還是得繞。</p>
<h2 id="那有沒有不該用-f-string-的時候">那有沒有不該用 f-string 的時候</h2>
<p>有，幾個我會避開。</p>
<p>寫 log 的時候。<code>logging.info(f&quot;processing {user}&quot;)</code> 會先把字串組好才丟進去，但如果這條 log 因為等級設定根本不會輸出，那串就白組了。<code>logging.info(&quot;processing %s&quot;, user)</code> 留給 logging 自己決定要不要組，省一點。差距平常摸不著，迴圈裡狂打 log 才現形。</p>
<p>還有把使用者輸入直接 f-string 拼進 SQL，這個是安全問題不是風格問題——<code>f&quot;SELECT ... WHERE id = {user_input}&quot;</code> 就是 SQL injection 的標準開法，這種一律走參數化查詢，別用 f-string。多語系（i18n / gettext）那種要把字串抽出去翻譯的場景也不適合，因為翻譯工具撈的是原始碼裡的靜態字串，f-string 沒留下那個能被抽出去對照的固定字面值。</p>
<p>這幾個之外，日常要把值湊成一段字串，f-string 大概都是最順的選擇。</p>
<p>真要記，記冒號那條，debug 時 <code>f&quot;{值=}&quot;</code> 練成反射，其他查得到。f-string 學一次能用很久，多花十分鐘往 <code>{}</code> 後面多看一眼，划算。</p>
<p>想再翻翻 Python 其他讓程式變短的小東西，可以順手看看 <a href="/python-list-comprehension/">Python 列表推導式：一行取代 for 迴圈</a>。</p>
<hr>
<p><em>本文範例都在 Python 3.14.3 上實際跑過。想看源頭的話：<a href="https://peps.python.org/pep-0498/">PEP 498 — Literal String Interpolation</a>（f-string 的起點）、<a href="https://peps.python.org/pep-0701/">PEP 701 — Syntactic formalization of f-strings</a>（3.12 的鬆綁）、<a href="https://docs.python.org/3/library/string.html#format-specification-mini-language">Format Specification Mini-Language</a>（冒號後面那套完整語法）。</em></p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
