<?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>CPython on KbWen Blog</title>
    <link>https://www.kbwen.com/tags/cpython/</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>Mon, 20 Jul 2026 11:00:00 +0800</lastBuildDate><atom:link href="https://www.kbwen.com/tags/cpython/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Python 的 is 和 == 差在哪</title>
      <link>https://www.kbwen.com/python-is-vs-equals/</link>
      <pubDate>Mon, 20 Jul 2026 11:00:00 +0800</pubDate><dc:creator>KbWen</dc:creator>
      <guid>https://www.kbwen.com/python-is-vs-equals/</guid>
      <description>is 比對物件身分，== 比對值。小整數快取讓某些整數上兩者剛好一致，但那是不保證的 CPython 實作細節，整數值該用 == 比。</description>
      <content:encoded><![CDATA[<p><code>x is y</code> 和 <code>x == y</code> 在 Python 裡問的是兩個不同的問題，只是拿整數來測的時候答案常常一樣，於是很容易被當成同一件事。<code>==</code> 比的是值，<code>is</code> 比的是身分，也就是兩個名字後面是不是同一個物件。</p>
<p>文件上寫，<code>is</code> 是「test for an object&rsquo;s identity」，<code>x is y</code>「is true if and only if」x 和 y「are the same object」，而是不是同一個物件，由 <code>id()</code> 決定（<a href="https://docs.python.org/3/reference/expressions.html#is">文件</a>）。<code>==</code> 走的是另一條路，它比的是兩個物件的值相不相等，值相等的兩個東西，不保證是同一個物件。</p>
<p>分界到底在哪，直接在直譯器裡跑一下就清楚了（以下都是 CPython 3.11）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="mi">256</span> <span class="ow">is</span> <span class="nb">int</span><span class="p">(</span><span class="s2">&#34;256&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kc">True</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="mi">257</span> <span class="ow">is</span> <span class="nb">int</span><span class="p">(</span><span class="s2">&#34;257&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kc">False</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="mi">257</span> <span class="o">==</span> <span class="mi">257</span>
</span></span><span class="line"><span class="cl"><span class="kc">True</span>
</span></span></code></pre></div><p><code>==</code> 永遠回傳 <code>True</code>，因為 257 就是等於 257，值一樣。但 <code>is</code> 在 256 上是 <code>True</code>、257 上是 <code>False</code>，分界剛好落在 256 和 257 之間。</p>
<p>原因是 CPython 的小整數快取。C API 的文件寫著，CPython「keeps an array of integer objects for all integers」，範圍是 -5 到 256，在這個範圍裡「just get back a reference to the existing object」（<a href="https://docs.python.org/3/c-api/long.html">文件</a>）。也就是說，256 這個物件在直譯器啟動時就已經建好放著了，每次寫 256、或用 <code>int(&quot;256&quot;)</code> 在執行期算出 256，拿到的都是那同一個物件，<code>is</code> 就成立。257 不在快取範圍內，每次要用就新建一個，兩個 257 是各自獨立的物件，身分自然不同。</p>
<p>這裡有個地方特別容易讓人測到看似矛盾的結果。如果在同一個函式裡直接寫兩個字面值：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="k">def</span> <span class="nf">f</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="n">a</span> <span class="o">=</span> <span class="mi">257</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="n">b</span> <span class="o">=</span> <span class="mi">257</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="k">return</span> <span class="n">a</span> <span class="ow">is</span> <span class="n">b</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;&gt;&gt;</span> <span class="n">f</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="kc">True</span>
</span></span></code></pre></div><p><code>a is b</code> 回傳 <code>True</code>，看起來好像 257 也被快取了，其實不是。這是常數摺疊，編譯器發現函式裡兩個 257 是同一個常數，就只存一份，<code>a</code> 和 <code>b</code> 指向的是同一個編譯期常數，跟小整數快取沒關係。同一個字面值多次求值「may obtain the same object or a different object with the same value」（<a href="https://docs.python.org/3/reference/expressions.html#literals">文件</a>）。</p>
<p>那個很有名的「257 is 257 回傳 False」示範會出問題，就是這個原因，結果時而 <code>True</code> 時而 <code>False</code>，取決於兩個 257 是不是同一份摺疊後的常數。想看到真正的快取邊界，就得用 <code>int(&quot;257&quot;)</code> 這種執行期才算出來的整數，繞過常數摺疊。</p>
<p>順帶一提，目前的 CPython 如果你直接寫 <code>x is 257</code>，直譯器會丟出 <code>SyntaxWarning: &quot;is&quot; with a literal. Did you mean &quot;==&quot;?</code>。</p>
<p>-5 到 256 這個範圍從來就不是保證，只是目前的實作。開發中的 main 分支已經把上界改成 1024。哪天邊界換了個版本就變了，靠 <code>is</code> 去比整數值的程式碼會默默壞掉，也不會有錯誤訊息提醒你。</p>
<p>所以實務上規則很簡單：要比整數值、或任何一般物件的值，用 <code>==</code>。<code>is</code> 留給判斷是不是同一個物件，最典型的就是跟 <code>None</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="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="ow">is</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"><span class="kc">True</span>
</span></span></code></pre></div><p>PEP 8 也是這樣建議，「Comparisons to singletons like None should always be done with」<code>is</code> 或 <code>is not</code>，「never the equality operators」（<a href="https://peps.python.org/pep-0008/#programming-recommendations">PEP 8</a>）。<code>None</code> 整支程式只有一個，拿身分去比它最直接也最快。整數值不屬於這一類，它只是剛好落在 <code>is</code> 對得上的那段範圍裡。</p>
<p>這篇簡單的分享，可能有錯、若發現會盡快修改~</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
