孔令文 發表於 2020-10-12 15:55:00

连Python产生器(Generator)的原理都解释不了,还敢说Python用了5年?

<div>&nbsp;<img src="https://img2020.cnblogs.com/blog/32818/202010/32818-20201012154740502-1800854551.png" alt="" loading="lazy">
<p>&nbsp;</p>
<p>&nbsp;</p>
</div>
<div>最近有很多学Python同学问我,Python Generator到底是什么东西,如何理解和使用。Ok,现在就用这篇文章对Python Generator做一个敲骨沥髓的深入解析。</div>
<div>&nbsp;</div>
<div>为了更好地理解产生器(Generator),还需要掌握另外两个东西:yield和迭代(iterables)。下面就迭代、产生器和yield分别做一个深入的解析。</div>
<div>&nbsp;</div>
<div><strong>1. 迭代</strong></div>
<div>&nbsp;</div>
<div>当创建一个列表对象后,可以一个接一个读取列表中的值,这个过程就叫做迭代。</div>
<div id="1970-1602484738177" data-theme="default" data-language="javascript">
<div class="cnblogs_code">
<pre>mylist =
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> mylist:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(i, end = <span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span>)</pre>
</div>
</div>
<div>&nbsp;</div>
<div>mylist对象是可迭代的。在创建列表时,可以使用列表推导表达式,所以从直观上看,列表是可迭代的。</div>
<div>&nbsp;</div>
<div id="8036-1602484738177" data-theme="default" data-language="javascript">
<div class="cnblogs_code">
<pre>mylist =
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> mylist:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(i, end=<span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span>)&nbsp;</pre>
</div>
</div>
<div>&nbsp;</div>
<div>只要使用for ... in...语句,那么in子句后面的部分一定是一个可迭代的对象,如列表、字典、字符串等。</div>
<div>&nbsp;</div>
<div>这些可迭代的对象在使用上非常容易理解,我们可以用自己期望的方式读取其中的值。但会带来一个严重的问题。就拿列表为例,如果需要迭代的值非常多,这就意味着需要先将所有的值都放到列表中,而且即使迭代完了列表中所有的值,这些值也不会从内存中消失(至少还会存在一会)。而且如果这些值只需要迭代一次就不再使用,那么这些值在内存中长期存在是没有必要的,所有就产生了产生器(Generator)的概念。</div>
<div>&nbsp;</div>
<div><strong>2. 产生器(Generator)</strong></div>
<div>&nbsp;</div>
<div>要理解产生器,首先要清楚产生器到底要解决什么问题,以及产生器的特性。</div>
<div>&nbsp;</div>
<div>产生器只解决一个问题,就是让需要迭代的值不再常驻内存,也就是解决的内存资源消耗的问题。</div>
<div>为了解决这个问题,产生器也要付出一定的代价,这个代价就是产生器中的值只能访问一次,这也是产生器的特性。</div>
<div>&nbsp;</div>
<div>下面先看一个最简单的产生器的例子:</div>
<div id="4760-1602484738177" data-theme="default" data-language="javascript">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建产生器</span>
data_generator = (x*x <span style="color: rgba(0, 0, 255, 1)">for</span> x <span style="color: rgba(0, 0, 255, 1)">in</span> range(3<span style="color: rgba(0, 0, 0, 1)">))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(data_generator)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> data_generator:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(i, end=<span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">()
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">第二次迭代data_generator,什么都不会输出</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">()
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> data_generator:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(i, end=<span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span>)</pre>
</div>
</div>
<div>&nbsp;</div>
<div>乍一看这段代码,好像与前面的代码没什么区别。其实,只有一点点区别,就是在创建data_generator对象时使用了一对圆括号,而不是一对方括号。使用一对方括号创建的是列表对象,而使用一对圆括号创建的就是迭代器对象,如果直接输出,会输出迭代器对象的地址,只有通过for...in...语句或调用迭代器的相应方法才能输出迭代器对象中的值。而且第二次对迭代器对象进行迭代,什么都不会输出,这是因为迭代器只能被迭代一次,而且被迭代的值使用完,是不会再保存在内存中的。有点类似熊瞎子摘苞米,摘一穗,丢一穗。</div>
<div>&nbsp;<img src="https://img2020.cnblogs.com/blog/32818/202010/32818-20201012154856915-2072597547.png" alt="" loading="lazy">
<p>&nbsp;</p>
<p>&nbsp;</p>
</div>
<div><img src="//note.youdao.com/src/WEBRESOURCE937654e04d98fc3cf17cc0cc83a5e5d3" alt="" data-media-type="image"></div>
<div>执行这段代码,会输出如下结果:</div>
<div>&nbsp;
<div class="cnblogs_code">
<pre>&lt;generator object &lt;genexpr&gt; at 0x7f95e0154150&gt;<span style="color: rgba(0, 0, 0, 1)">
0 </span>1 4<span style="color: rgba(0, 0, 0, 1)">
第二次迭代data_generator,什么都不会输出</span></pre>
</div>
</div>
<div><strong>&nbsp;</strong></div>
<div><strong>3. yield</strong></div>
<div>&nbsp;</div>
<div>到现在为止,我们已经对产生器要解决的问题,以及特性有了一个基本了解,那么产生器是如何做到这一点的呢?这就要依靠yield语句了。</div>
<div>&nbsp;</div>
<div>现在让我们先来看一个使用yield的例子。</div>
<div>&nbsp;
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 编写产生器函数</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> generate_even(max):
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(0, max + 1<span style="color: rgba(0, 0, 0, 1)">):
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> i % 2 ==<span style="color: rgba(0, 0, 0, 1)"> 0:
            </span><span style="color: rgba(0, 0, 255, 1)">yield</span><span style="color: rgba(0, 0, 0, 1)"> i
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(generate_even(10<span style="color: rgba(0, 0, 0, 1)">))
even_generator </span>= generate_even(10<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> n <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> even_generator:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(n, end=<span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span>)</pre>
</div>
</div>
<div>&nbsp;</div>
<div>这段代码的目的是输出不大于10的所有偶数,其中generator_even是一个产生器函数。我们注意到,在该函数中每找到一个偶数,就会通过yield语句指定这个偶数。那么这个yield起什么作用呢?</div>
<div>&nbsp;</div>
<div>再看看后面的代码,首先调用generator_even函数,并将返回值赋给even_generator变量,这个变量的类型其实是一个产生器对象。而for...in...循环中的in子句后面则是这个产生器对象,而n则是产生器中的每一个值(偶数)。执行这段代码,会输出如下结果:</div>
<div>&nbsp;
<div class="cnblogs_code">
<pre>&lt;generator object generate_even at 0x7f814826c450&gt;<span style="color: rgba(0, 0, 0, 1)">
0 </span>2 4 6 8 10 </pre>
</div>
</div>
<div>&nbsp;</div>
<div>现在先谈谈执行yield语句会起到什么效果。其实yield语句与return语句一样,都起到返回的作用。但yield与return不同,如果执行return语句,会直接返回return后面表达式的值。但执行yield语句,返回的是一个产生器对象,而且这个产生器对象的当前值就是yield语句后面跟着的表达式的值。调用yield语句后,当前函数就会返回一个迭代器,而且函数会暂停执行,直到对该函数进行下一次迭代。</div>
<div>&nbsp;</div>
<div>可能读到这些解释,有的读者还是不太明白,什么时候进行下一次迭代呢?如果不使用for...in...语句,是否可以对产生器进行迭代呢?其实迭代器有一个特殊方法__next__。每次对迭代器的迭代,本质上都是在调用__next__方法。</div>
<div>&nbsp;</div>
<div>那么还有最后一个问题,for...in...语句在什么时候才会停止迭代呢?其实for...in...语句在底层会不断调用in子句后面的可迭代对象的__next__方法,直到该方法抛出StopIteration异常为止。也就是说,可以将上面的for...in...循环改成下面的代码。连续调用6次__next__方法,返回0到10,一共6个偶数,当第7次调用__next__方法时,产生器中已经没有值了,所以会抛出StopIteration异常。由于for...in...语句自动处理了StopIteration异常,所以循环就会自动停止,但当直接调用__next__方法时,如果产生器中没有值了,就会直接抛出StopIteration异常,除非使用try...except...语句捕获该异常,否则程序会异常中断。</div>
<div>
<div class="cnblogs_code">
<pre>even_generator = generate_even(10<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(even_generator.<span style="color: rgba(128, 0, 128, 1)">__next__</span><span style="color: rgba(0, 0, 0, 1)">())
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(even_generator.<span style="color: rgba(128, 0, 128, 1)">__next__</span><span style="color: rgba(0, 0, 0, 1)">())
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(even_generator.<span style="color: rgba(128, 0, 128, 1)">__next__</span><span style="color: rgba(0, 0, 0, 1)">())
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(even_generator.<span style="color: rgba(128, 0, 128, 1)">__next__</span><span style="color: rgba(0, 0, 0, 1)">())
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(even_generator.<span style="color: rgba(128, 0, 128, 1)">__next__</span><span style="color: rgba(0, 0, 0, 1)">())
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(even_generator.<span style="color: rgba(128, 0, 128, 1)">__next__</span><span style="color: rgba(0, 0, 0, 1)">())
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> print(even_generator.__next__())# 抛出StopIteration异常</span></pre>
</div>
<p>总结:产生器本质上就是动态产生待迭代的值,使用完就直接扔掉了,这样非常节省内存空间,但这些值只能被迭代一次。</p>
</div>
<div>&nbsp;</div>
<div><strong>4. 用普通函数模拟产生器函数的效果</strong></div>
<div>&nbsp;</div>
<div>如果你看到一个函数中使用了yield语句,说明该函数是一个产生器。其实可以按下面的步骤将该产生器函数改造成普通函数。</div>
<div>&nbsp;</div>
<div>1. 在函数的开始部分定义一个列表变量,代码如下:
<div class="cnblogs_code">
<pre>result = []</pre>
</div>
</div>
<div>2. 将所有的yield expr语句都替换成下面的语句:</div>
<div>
<div class="cnblogs_code">
<pre>result.append(expr)</pre>
</div>
</div>
<div>3. 函数的最后执行return result返回这个列表对象</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>为了更清晰表明这个转换过程,现在给出一个实际的案例:</div>
<div>&nbsp;</div>
<div id="4874-1602485581975" data-theme="default" data-language="javascript">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 产生不大于max的偶数</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> generate_even(max):
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(0, max + 1<span style="color: rgba(0, 0, 0, 1)">):
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> i % 2 ==<span style="color: rgba(0, 0, 0, 1)"> 0:
            </span><span style="color: rgba(0, 0, 255, 1)">yield</span><span style="color: rgba(0, 0, 0, 1)"> i

even_generator </span>= generate_even(10<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> n <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> even_generator:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(n, end=<span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)

</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 将产生器函数改造成普通函数,实际上,就是将yield后面表达式的值都添加在列表中</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> generate_even1(max):
    evens </span>=<span style="color: rgba(0, 0, 0, 1)"> []
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(0, max + 1<span style="color: rgba(0, 0, 0, 1)">):
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> i % 2 ==<span style="color: rgba(0, 0, 0, 1)"> 0:
            evens.append(i)
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> evens
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">()
even_list </span>= generate_even1(10<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> n <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> even_list:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(n, end=<span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span>)</pre>
</div>
<p>在这段代码中有两个函数:generate_even和generate_even1,其中generate_even是产生器函数,generate_even1是普通函数(与generate_even函数的功能完全相同)。按着前面的步骤,将所有产生的偶数都添加到了列表变量evens中,最后返回这个列表变量。这两个函数在使用方式上完全相同。不过从本质上说,generate_even函数是动态生成的偶数,用完了就扔,而generate_even1函数事先将所有产生的偶数都添加到列表中,最后返回。所以从generate_even1函数的改造过程来看,yield的作用就相当于使用append方法将表达式的值添加到列表中,只不过yield并不会保存表达式的值,而append方法会保存表达式的值。</p>
</div>
<div>&nbsp;</div>
<div><strong>&nbsp;5.与迭代相关的API</strong></div>
<div>&nbsp;</div>
<div>这一节来看一看Python为我们提供了哪些与迭代相关的API</div>
<div>&nbsp;</div>
<div>Python SDK提供了一个itertools模块,该模块中的API都与迭代相关,例如,可以通过chain.from_iterable方法合并多个可迭代对象,通过permutations函数以可迭代对象形式返回列表的全排列。</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">from</span> itertools <span style="color: rgba(0, 0, 255, 1)">import</span> *

<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 这里每一个yield的值必须是可迭代的,才能用chain.from_iterable方法合并</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> make_iterables_to_chain():
    </span><span style="color: rgba(0, 0, 255, 1)">yield</span>
    </span><span style="color: rgba(0, 0, 255, 1)">yield</span> [<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(128, 0, 0, 1)">'</span>,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">b</span><span style="color: rgba(128, 0, 0, 1)">'</span>,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">c</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">]
    </span><span style="color: rgba(0, 0, 255, 1)">yield</span> [<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">hello</span><span style="color: rgba(128, 0, 0, 1)">'</span>,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">world</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">]

</span><span style="color: rgba(0, 0, 255, 1)">for</span> v <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> make_iterables_to_chain():
    </span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(v)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 将所有可迭代对象合并成一个可迭代对象</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> v <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> chain.from_iterable(make_iterables_to_chain()):
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">'</span>,v,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">&gt;</span><span style="color: rgba(128, 0, 0, 1)">'</span>, end = <span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">-------上面的代码相当于下面的写法-------</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
a </span>=
a.extend([</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(128, 0, 0, 1)">'</span>,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">b</span><span style="color: rgba(128, 0, 0, 1)">'</span>,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">c</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">])
a.extend([</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">hello</span><span style="color: rgba(128, 0, 0, 1)">'</span>,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">world</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">])
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(a)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> v <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> a:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">[</span><span style="color: rgba(128, 0, 0, 1)">'</span>,v,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">]</span><span style="color: rgba(128, 0, 0, 1)">'</span>, end = <span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">以可迭代对象形式返回列表的全排列</span>
values =
values_permutations </span>=<span style="color: rgba(0, 0, 0, 1)"> permutations(values)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> p <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> values_permutations:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span>(p)</pre>
</div>
<p>&nbsp;</p>
</div>
<div>执行这段代码,会输出如下内容:</div>
<div id="6999-1602484738178" data-theme="default" data-language="javascript">
<div class="cnblogs_code">
<pre>
[</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">b</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">c</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">]
[</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">hello</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">world</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">]
</span>&lt; 1 &gt; &lt; 2 &gt; &lt; 3 &gt; &lt; a &gt; &lt; b &gt; &lt; c &gt; &lt; hello &gt; &lt; world &gt; -------上面的代码相当于下面的写法-------<span style="color: rgba(0, 0, 0, 1)">
[</span>1, 2, 3, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">b</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">c</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">hello</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">world</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">]
[ </span>1 ] [ 2 ] [ 3 ] [ a ] [ b ] [ c ] [ hello ] [ world ] (1, 2, 3, 4<span style="color: rgba(0, 0, 0, 1)">)
(</span>1, 2, 4, 3<span style="color: rgba(0, 0, 0, 1)">)
(</span>1, 3, 2, 4<span style="color: rgba(0, 0, 0, 1)">)
(</span>1, 3, 4, 2<span style="color: rgba(0, 0, 0, 1)">)
(</span>1, 4, 2, 3<span style="color: rgba(0, 0, 0, 1)">)
(</span>1, 4, 3, 2<span style="color: rgba(0, 0, 0, 1)">)
(</span>2, 1, 3, 4<span style="color: rgba(0, 0, 0, 1)">)
(</span>2, 1, 4, 3<span style="color: rgba(0, 0, 0, 1)">)
(</span>2, 3, 1, 4<span style="color: rgba(0, 0, 0, 1)">)
(</span>2, 3, 4, 1<span style="color: rgba(0, 0, 0, 1)">)
(</span>2, 4, 1, 3<span style="color: rgba(0, 0, 0, 1)">)
(</span>2, 4, 3, 1<span style="color: rgba(0, 0, 0, 1)">)
(</span>3, 1, 2, 4<span style="color: rgba(0, 0, 0, 1)">)
(</span>3, 1, 4, 2<span style="color: rgba(0, 0, 0, 1)">)
(</span>3, 2, 1, 4<span style="color: rgba(0, 0, 0, 1)">)
(</span>3, 2, 4, 1<span style="color: rgba(0, 0, 0, 1)">)
(</span>3, 4, 1, 2<span style="color: rgba(0, 0, 0, 1)">)
(</span>3, 4, 2, 1<span style="color: rgba(0, 0, 0, 1)">)
(</span>4, 1, 2, 3<span style="color: rgba(0, 0, 0, 1)">)
(</span>4, 1, 3, 2<span style="color: rgba(0, 0, 0, 1)">)
(</span>4, 2, 1, 3<span style="color: rgba(0, 0, 0, 1)">)
(</span>4, 2, 3, 1<span style="color: rgba(0, 0, 0, 1)">)
(</span>4, 3, 1, 2<span style="color: rgba(0, 0, 0, 1)">)
(</span>4, 3, 2, 1)</pre>
</div>
<p>&nbsp;</p>
</div>
<div>&nbsp;更多免费视频课程,请识别下面二维码看我的B站</div>
<div><img src="https://img2020.cnblogs.com/blog/32818/202010/32818-20201012155351295-1931885252.png" alt="" loading="lazy">
<p>&nbsp;</p>
<p>&nbsp;</p>
对本文感兴趣,可以加李宁老师微信公众号(unitymarvel)</div>
<div><img src="https://img2020.cnblogs.com/blog/32818/202010/32818-20201012155422376-855565996.png" alt="" loading="lazy">
<p>&nbsp;</p>
<p>&nbsp;</p>
关注&nbsp;&nbsp;「极客起源」&nbsp; 公众号,获得更多免费技术视频和文章。<br><img src="https://img2020.cnblogs.com/blog/32818/202010/32818-20201012155444521-1551796524.png" alt="" loading="lazy">
<p>&nbsp;</p>
<p>&nbsp;&nbsp;</p>
</div>
<p></p><br><br>
来源:https://www.cnblogs.com/nokiaguy/p/13803370.html
頁: [1]
查看完整版本: 连Python产生器(Generator)的原理都解释不了,还敢说Python用了5年?