<?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>Iterator on KbWen Blog</title>
    <link>https://www.kbwen.com/tags/iterator/</link>
    <description>KbWen is a practical technology blog about AI systems, machine learning, Python, data engineering, and software development.</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>Sat, 11 Apr 2020 16:08:13 +0800</lastBuildDate><atom:link href="https://www.kbwen.com/tags/iterator/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Python Iterable</title>
      <link>https://www.kbwen.com/python-iterable/</link>
      <pubDate>Sat, 11 Apr 2020 16:08:13 +0800</pubDate><dc:creator>KbWen</dc:creator>
      <guid>https://www.kbwen.com/python-iterable/</guid>
      <description>只實作 __getitem__ 的物件，for 迴圈跑得動、iter() 也拿得到 iterator，但 isinstance(obj, Iterable) 會回 False。本文用跑得出來的程式碼示範這個落差，並說明 iterator 為什麼只能走一遍。</description>
      <content:encoded><![CDATA[<p>要了解python 哪些對象是可以迭代的，</p>
<p>可以先了解兩個相似的名詞</p>
<ol>
<li>Iterable</li>
<li>Iterator</li>
</ol>
<h2 id="iterable">Iterable</h2>
<p>可以被迭代、遍歷(loop, iteration)的物件對象可以被稱為iterable，</p>
<p>從官方文件得知，要實現<code>__iter__</code>或是<code>__getitem__</code>的方法即可。</p>
<p>包含了常見的list、tuple、set、dict、str、range，</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="nb">dir</span><span class="p">(</span><span class="nb">str</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="s1">&#39;__add__&#39;</span><span class="p">,</span> <span class="s1">&#39;__class__&#39;</span><span class="p">,</span> <span class="s1">&#39;__contains__&#39;</span><span class="p">,</span> <span class="s1">&#39;__delattr__&#39;</span><span class="p">,</span> <span class="s1">&#39;__dir__&#39;</span><span class="p">,</span> <span class="s1">&#39;__doc__&#39;</span><span class="p">,</span> <span class="s1">&#39;__eq__&#39;</span><span class="p">,</span> <span class="s1">&#39;__format__&#39;</span><span class="p">,</span> <span class="s1">&#39;__ge__&#39;</span><span class="p">,</span> <span class="s1">&#39;__getattribute__&#39;</span><span class="p">,</span> <span class="s1">&#39;__getitem__&#39;</span><span class="p">,</span> <span class="s1">&#39;__getnewargs__&#39;</span><span class="p">,</span> <span class="s1">&#39;__gt__&#39;</span><span class="p">,</span> <span class="s1">&#39;__hash__&#39;</span><span class="p">,</span> <span class="s1">&#39;__init__&#39;</span><span class="p">,</span> <span class="s1">&#39;__init_subclass__&#39;</span><span class="p">,</span> <span class="s1">&#39;__iter__&#39;</span><span class="p">,</span> <span class="o">......</span><span class="p">]</span>
</span></span></code></pre></div><blockquote>
<p>但若是使用collection去檢查是否是iterable</p>
<p>只有實現<code>__getitem__</code>的對象可以被迭代但不會是iterable</p>
</blockquote>
<h2 id="只有-__getitem__-的物件">只有 <code>__getitem__</code> 的物件</h2>
<p><code>Deck</code> 裡放三張牌，只實作 <code>__getitem__</code>，把收到的索引轉給底下的 list（以下的輸出都來自 Python 3.14）：</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">class</span> <span class="nc">Deck</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>         <span class="bp">self</span><span class="o">.</span><span class="n">cards</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;A&#34;</span><span class="p">,</span> <span class="s2">&#34;K&#34;</span><span class="p">,</span> <span class="s2">&#34;Q&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">i</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>         <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">cards</span><span class="p">[</span><span class="n">i</span><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="o">&gt;&gt;&gt;</span> <span class="k">for</span> <span class="n">card</span> <span class="ow">in</span> <span class="n">Deck</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="nb">print</span><span class="p">(</span><span class="n">card</span><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="n">A</span>
</span></span><span class="line"><span class="cl"><span class="n">K</span>
</span></span><span class="line"><span class="cl"><span class="n">Q</span>
</span></span></code></pre></div><p>這個類別沒有 <code>__iter__</code>，<code>for</code> 迴圈照樣把三張牌跑完了。<code>for</code> 拿到一個物件的時候，會先呼叫 <code>iter()</code> 跟它要一個 iterator，接下來就一直對這個 iterator 呼叫 <code>__next__</code>，直到 <code>StopIteration</code> 出現為止。<code>iter()</code> 在物件身上找不到 <code>__iter__</code> 的話，還有第二條路可以走，改成用索引一個一個取，這條路在 <code>iter()</code> 的說明裡跟 <code>__iter__</code> 並列，「or it must support the sequence protocol (the __getitem__() method with integer arguments starting at 0)」。</p>
<p>這條路走起來長什麼樣子，把 <code>__getitem__</code> 加一行 print 就看得到，同樣的迴圈再跑一次：</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">class</span> <span class="nc">Deck</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>         <span class="bp">self</span><span class="o">.</span><span class="n">cards</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;A&#34;</span><span class="p">,</span> <span class="s2">&#34;K&#34;</span><span class="p">,</span> <span class="s2">&#34;Q&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">i</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>         <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;__getitem__(</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>         <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">cards</span><span class="p">[</span><span class="n">i</span><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="o">&gt;&gt;&gt;</span> <span class="k">for</span> <span class="n">card</span> <span class="ow">in</span> <span class="n">Deck</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="nb">print</span><span class="p">(</span><span class="n">card</span><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="fm">__getitem__</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">A</span>
</span></span><span class="line"><span class="cl"><span class="fm">__getitem__</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">K</span>
</span></span><span class="line"><span class="cl"><span class="fm">__getitem__</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Q</span>
</span></span><span class="line"><span class="cl"><span class="fm">__getitem__</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
</span></span></code></pre></div><p>索引從 0 開始，一次加一，三張牌拿完之後 <code>for</code> 又要了索引 3，底下的 list 這時丟出 <code>IndexError</code>，迴圈就在這裡停下來。這個停止條件寫在語言參考 <code>__getitem__</code> 的說明底下，「The sequence iteration protocol (used, for example, in for loops), expects that an IndexError will be raised for illegal indexes to allow proper detection of the end of a sequence.」自己寫 <code>__getitem__</code> 的時候，索引超出範圍要讓 <code>IndexError</code> 出來，回傳 <code>None</code> 或是丟別的例外，迴圈都停不下來。</p>
<p>中間那個 iterator 是 Python 自己補上的，也可以抓出來一步一步走：</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">d</span> <span class="o">=</span> <span class="n">Deck</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">type</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">d</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="k">class</span> <span class="err">&#39;</span><span class="nc">iterator</span><span class="s1">&#39;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">it</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;A&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;K&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;Q&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">next</span><span class="p">(</span><span class="n">it</span><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="ne">StopIteration</span>
</span></span></code></pre></div><p>型別的名字就叫 <code>iterator</code>，裡面存著一個從 0 開始的計數器，每次 <code>__next__</code> 拿這個數字去呼叫 <code>Deck.__getitem__</code>，接到 <code>IndexError</code> 之後換成 <code>StopIteration</code> 丟出來。<code>Deck</code> 自己沒有 <code>__next__</code>，前面那個 <code>for</code> 迴圈的每一步都發生在這個包裝物件上。每呼叫一次 <code>iter(d)</code> 就多一個新的包裝物件，計數器各自從 0 開始，所以同一個 <code>Deck</code> 連著跑兩次 <code>for</code> 迴圈，兩次都拿到完整的三張牌。</p>
<p>到這裡 <code>Deck</code> 的行為和一個普通序列沒什麼兩樣，換成用 <code>collections.abc</code> 去問它是不是 iterable，答案就不一樣了：</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="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Iterable</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span> <span class="o">=</span> <span class="n">Deck</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">Iterable</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="nb">iter</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">iterator</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x000001363C1A40D0</span><span class="o">&gt;</span>
</span></span></code></pre></div><p><code>isinstance</code> 回 False，<code>iter()</code> 卻拿得到東西，前面那個 <code>for</code> 也確實跑完了。差別在 <code>Iterable</code> 這個 ABC 檢查的範圍比較窄，標準庫裡它的 <code>__subclasshook__</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="kn">import</span> <span class="nn">inspect</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Iterable</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">inspect</span><span class="o">.</span><span class="n">getsource</span><span class="p">(</span><span class="n">Iterable</span><span class="o">.</span><span class="n">__subclasshook__</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@classmethod</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">__subclasshook__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">C</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="bp">cls</span> <span class="ow">is</span> <span class="n">Iterable</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">_check_methods</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="s2">&#34;__iter__&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">NotImplemented</span>
</span></span></code></pre></div><p><code>_check_methods(C, &quot;__iter__&quot;)</code> 是到類別的 MRO 上找 <code>__iter__</code>，找不到就回 False，<code>__getitem__</code> 完全不在檢查範圍內。這裡被問的是 <code>Deck</code> 這個類別，剛才那個包裝物件則兩個方法都有，拿它去問 <code>isinstance</code>，<code>Iterable</code> 和 <code>Iterator</code> 都會回 True。</p>
<p>平常用內建型別碰不到這個落差。前面 <code>dir(str())</code> 印出來的清單裡，<code>__iter__</code> 和 <code>__getitem__</code> 兩個都在，list、tuple、range 也都有 <code>__iter__</code>，<code>isinstance</code> 問到的一律是 True。要自己寫一個只有 <code>__getitem__</code> 的類別，兩邊的答案才會分開。</p>
<p>會踩到的地方是在函式入口做型別檢查那種寫法，收到參數先問 <code>isinstance(x, Iterable)</code>，答案是 False 就把參數退回去，這樣會把只有 <code>__getitem__</code> 的物件擋在外面，而那個物件用 <code>for</code> 跑得動。想知道一個東西能不能迭代，照文件的說法是呼叫 <code>iter(obj)</code> 看看，拿得到 iterator 就是可以，兩種協定都不支援的話 <code>iter()</code> 會自己丟出 <code>TypeError</code>。類別如果是自己寫的，補一個 <code>__iter__</code> 進去，<code>isinstance</code> 也就通過了。</p>
<h2 id="iterator">Iterator</h2>
<p><a href="https://docs.python.org/3/library/stdtypes.html#iterator-types">https://docs.python.org/3/library/stdtypes.html#iterator-types</a></p>
<p>從官方文件看出，含有<code>__iter__</code>和<code>__next__</code>的對象可稱為iterator，</p>
<p>iterator是iterable的子集合，</p>
<p>上述提到的幾種方式是iterable但都不是iterator，可以使用上面用到的<code>isinstance</code>或是<code>dir</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="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Iterable</span><span class="p">,</span> <span class="n">Iterator</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">],</span> <span class="s2">&#34;123&#34;</span><span class="p">,</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <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">i</span><span class="si">}</span><span class="s2"> is iterable: </span><span class="si">{</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">Iterable</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <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">i</span><span class="si">}</span><span class="s2"> is iterator: </span><span class="si">{</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">Iterator</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><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 class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="ow">is</span> <span class="n">iterable</span><span class="p">:</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="ow">is</span> <span class="n">iterator</span><span class="p">:</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl"><span class="mi">123</span> <span class="ow">is</span> <span class="n">iterable</span><span class="p">:</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl"><span class="mi">123</span> <span class="ow">is</span> <span class="n">iterator</span><span class="p">:</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="ow">is</span> <span class="n">iterable</span><span class="p">:</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="ow">is</span> <span class="n">iterator</span><span class="p">:</span> <span class="kc">False</span>
</span></span></code></pre></div><p>而檔案物件則是 iterator</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">file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="s2">&#34;test.py&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">)</span> <span class="k">as</span> <span class="n">ifile</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>     <span class="nb">isinstance</span><span class="p">(</span><span class="n">ifile</span><span class="p">,</span> <span class="n">Iterator</span><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="kc">True</span>
</span></span></code></pre></div><h2 id="iterator-只能走一遍">iterator 只能走一遍</h2>
<p>iterator 除了 <code>__next__</code> 之外還要有 <code>__iter__</code>，而且它的 <code>__iter__</code> 回傳的是自己，所以每個 iterator 同時也是 iterable，可以直接丟進 <code>for</code> 迴圈。同一個 iterator 只能這樣走一遍，走完之後再丟進 <code>for</code>，一個值都拿不到。</p>
<p>把 list 和它的 iterator 擺在一起比較：</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">nums</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">iter</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="ow">is</span> <span class="nb">iter</span><span class="p">(</span><span class="n">nums</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="n">it</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">iter</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="ow">is</span> <span class="n">it</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="nb">list</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">list</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">[]</span>
</span></span></code></pre></div><p><code>iter(nums)</code> 呼叫兩次拿到兩個不同的物件，<code>is</code> 比出來是 False，每次都是新的一個，各自從頭開始數。<code>iter(it)</code> 就不一樣，回來的是 <code>it</code> 本身，<code>is</code> 比出來是 True。第一次 <code>list(it)</code> 把三個值取完，第二次再取，<code>it</code> 已經走到底，<code>list()</code> 拿回來的是 <code>[]</code>。</p>
<p>容器每次被丟進 <code>iter()</code> 或是 <code>for</code> 迴圈，都會交出一個新的 iterator；同樣的動作換成 iterator，拿回來的是上一輪已經走完的那一個，看起來就像一個空的容器。</p>
<p>這件事在函式之間傳參數的時候容易撞到。函式收到一個東西，先跑一次 <code>for</code> 算數量，再跑一次 <code>for</code> 做別的處理，參數是 list 的話兩次都正常，因為 <code>for</code> 每次都跟 list 要一個新的 iterator；換成 <code>map()</code>、<code>filter()</code>、<code>zip()</code> 回來的東西，或是一個 generator，或是前面那個用 <code>open()</code> 打開的檔案物件，第二次迴圈就一個值都拿不到。這不會丟出例外，資料看起來只是變成空的。需要走第二遍的話，先用 <code>list()</code> 把值收下來再用，或是每次迭代之前重新產生一個 iterator。</p>
<h2 id="結語">結語</h2>
<p>了解了iterable和iterator，以後開發時，</p>
<p>若想創造出可以被迭代的對象或是迭代器，</p>
<p>則要知道必須要包含哪些基礎功能</p>
<p>那麼Generator呢？</p>
<h2 id="系列文章">系列文章</h2>
<ul>
<li><a href="/python-pdb/">Python pdb</a></li>
<li><a href="/python-context-manager/">Python Context Manager</a></li>
<li><a href="/python-lambda/">Python Lambda</a></li>
<li><a href="/python-f-string/">Python f-string</a></li>
<li><a href="/python-comments/">Python Comments</a></li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
