<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>NotionNext BLOG</title>
        <link>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/</link>
        <description>这是一个由NotionNext生成的站点</description>
        <lastBuildDate>Fri, 27 Jun 2025 04:03:26 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2025, 冰冰冰淇淋</copyright>
        <item>
            <title><![CDATA[Python转义的五种表示法]]></title>
            <link>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a1ed64f-c993-80c7-ad65-f515d692073f</link>
            <guid>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a1ed64f-c993-80c7-ad65-f515d692073f</guid>
            <pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[当一个字符串中具有转义的字符时，我们使用print打印后，正常情况下，输出的不是我们原来在字符串中看到的那样子。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1a1ed64fc99380c7ad65f515d692073f"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-1a1ed64fc99381509e80d5f26390297f"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">这里写文章的前言：
一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么？并简述你给出的答案。<div class="notion-text notion-block-1a1ed64fc9938133afd9d1284443edb2">可以说说你的故事：阻碍、努力、结果成果，意外与转折。</div></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a1ed64fc99381219ae7df03c48fa180" data-id="1a1ed64fc99381219ae7df03c48fa180"><span><div id="1a1ed64fc99381219ae7df03c48fa180" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc99381219ae7df03c48fa180" title="📝 主旨内容"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📝 主旨内容</span></span></h2><div class="notion-text notion-block-1a1ed64fc993805d8b64e46dc13c3937">ASCII 表中一共有 128 个字符。这里面有我们非常熟悉的字母、数字、标点符号，这些都可以从我们的键盘中输出。除此之外，还有一些非常特殊的字符，这些字符，我通常很难用键盘上的找到，比如制表符、响铃这种。</div><div class="notion-text notion-block-1a1ed64fc99380899efbf8deaf05d233">为了能将那些特殊字符都能写入到字符串变量中，就规定了一个用于转义的字符 <code class="notion-inline-code">\</code> ，有了这个字符，你在字符串中看的字符，print 出来后就不一定你原来看到的了。</div><div class="notion-text notion-block-1a1ed64fc993801784cde0e32246262b">举个例子</div><div class="notion-text notion-block-1a1ed64fc99380c685f7d43e61b841a1">是不是有点神奇？变成阶梯状的输出了。</div><div class="notion-text notion-block-1a1ed64fc9938009b287f5326038727a">那个<code class="notion-inline-code">\013</code>又是什么意思呢？</div><ul class="notion-list notion-list-disc notion-block-1a1ed64fc99380468717c88f92b801f3"><li><code class="notion-inline-code">\</code>是转义符号，上面已经说过</li></ul><ul class="notion-list notion-list-disc notion-block-1a1ed64fc993808aa0b6e96451234bd8"><li><code class="notion-inline-code">013</code>是ASCII编码的八进制表示，注意前面是<code class="notion-inline-code">0</code>且不可省略，而不是字母<code class="notion-inline-code">o</code></li></ul><div class="notion-text notion-block-1a1ed64fc9938037947dfa46be8af938">把八进制的13转成10进制后是 11</div><div class="notion-text notion-block-1a1ed64fc99380e39fa9f7510a61360a">对照查看ASCII码表，11对应的是一个垂直定位符号，这就能解释，为什么是阶梯状的输出字符串。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1a1ed64fc99380a581d3efc7c473a21d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="attachment:f39102af-8df0-45c9-a4cd-df4eb0304dbe:v2-a4697eec8b9d9569099618f3bd098994_r.jpg?t=1a1ed64f-c993-80a5-81d3-efc7c473a21d" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc993809aaa07c1f515ab5ec6" data-id="1a1ed64fc993809aaa07c1f515ab5ec6"><span><div id="1a1ed64fc993809aaa07c1f515ab5ec6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993809aaa07c1f515ab5ec6" title="2. 转义的 5 种表示法"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2. 转义的 5 种表示法</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc99380ab9f5cfcee90a0f79e">ASCII 有 128 个字符，如果用 八进制表示，至少得有三位数，才能将其全部表示。这就是为什么说上面的首位 <code class="notion-inline-code">0</code> 不能省略的原因，即使现在用不上，我也得把它空出来。</div><div class="notion-text notion-block-1a1ed64fc993805b8ea0ff3890d181b8">而如果使用十六进制，只要两位数就其 ASCII 的字符全部表示出来。同时为了避免和八进制的混淆起来，所以在 <code class="notion-inline-code">\</code> 后面要加上英文字母 <code class="notion-inline-code">x</code> 表示<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=157662006&amp;content_type=Article&amp;match_order=2&amp;q=%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6&amp;zhida_source=entity">十六进制</a>，后面再接两位十六进制的数值。</div><ul class="notion-list notion-list-disc notion-block-1a1ed64fc99380c4bc79f17303482200"><li><code class="notion-inline-code">\</code> 开头并接三位0-7的数值，表示8进制</li></ul><ul class="notion-list notion-list-disc notion-block-1a1ed64fc993808ea5b8e6abdc5fc1ac"><li><code class="notion-inline-code">\x</code> 开头并接两位0-f的数值，表示16进制</li></ul><div class="notion-text notion-block-1a1ed64fc99380ef87faf63fa1c956b8">因此，当我定义一个字符串的值为 <code class="notion-inline-code">hello</code> + 回车 + <code class="notion-inline-code">world</code> 时，就有了多种方法：</div><div class="notion-text notion-block-1a1ed64fc9938023a067ebc8138f6eb6">通常我们很难记得住一个字符的ASCII编号，即使真记住了，也要去转换成八进制或者16进制，实在是太难了。</div><div class="notion-text notion-block-1a1ed64fc99380c1be81f2b7e31b5acc">因此对于一些常用并且比较特殊字符，我们习惯用另一种类似别名的方式，比如使用<code class="notion-inline-code">\n</code>表示换行，它与<code class="notion-inline-code">\012</code> 、<code class="notion-inline-code">\x0a</code>是等价的。</div><div class="notion-text notion-block-1a1ed64fc993807bac61f81e078aa26c">与此类似的表示法，还有如下这些:</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1a1ed64fc993807e91e8e8d8dcad6617"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="attachment:11778cf0-97f0-4100-9083-cc9605f7abfc:v2-a51bd5d43d2b6265bca901570129289d_1440w.jpg?t=1a1ed64f-c993-807e-91e8-e8d8dcad6617" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1a1ed64fc99380e882a4ec5c7bcdfe7a">于是，要实现 <code class="notion-inline-code">hello</code> + 回车 + <code class="notion-inline-code">world</code> ，就有了第三种方法</div><div class="notion-text notion-block-1a1ed64fc99380e0933ef8a4587772f0">到目前为止，我们掌握了三种转义的表示法。</div><div class="notion-text notion-block-1a1ed64fc99380baad38ebe8474bbd9b">已经非常难得了，让我们的脑洞再大一点吧，接下来再介绍两种。</div><div class="notion-text notion-block-1a1ed64fc99380f6b40bffb3b3d2060f">ASCII 码表所能表示字符实在太有限了，想打印一个中文汉字，抱歉，你得借助Unicode码。</div><div class="notion-text notion-block-1a1ed64fc9938061ab86d5fc7c709681">Unicode编码由4个16进制数值组合而成</div><div class="notion-text notion-block-1a1ed64fc99380589fa3d73798db0669">什么？我为什么知道<code class="notion-inline-code">中</code>的 unicode 是<code class="notion-inline-code">\u4E2D</code>？像下面这样打印就知道啦</div><div class="notion-text notion-block-1a1ed64fc993803a8f8bec2dd64473bd">由此，要实现<code class="notion-inline-code">hello</code> + 回车 + <code class="notion-inline-code">world</code> ，就有了第四种方法。</div><div class="notion-text notion-block-1a1ed64fc9938014a44cf006a0c15451">看到这里，你是不是以为要结束啦？</div><div class="notion-text notion-block-1a1ed64fc99380f78ce2cae85fe646d2">不，还没有。下面还有一种。</div><div class="notion-text notion-block-1a1ed64fc9938096b6b6ed60fde686c9">Unicode 编码其实还可以由 8 个<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=157662006&amp;content_type=Article&amp;match_order=1&amp;q=32%E8%BF%9B%E5%88%B6&amp;zhida_source=entity">32进制</a>数值组合而成，为了以前面的区分开来，这里用<code class="notion-inline-code">\U</code>开头。</div><div class="notion-text notion-block-1a1ed64fc99380bfa3dbdeb4628c9127">好啦，目前我们掌握了五种转义的表示法。</div><div class="notion-text notion-block-1a1ed64fc99380be868dc1d2d3e89442">为什么说转义也可以炫技呢？</div><div class="notion-text notion-block-1a1ed64fc99380c5ae0df8e789b2a158">试想一下，假如你的同事，在打印日志时，使用这种 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=157662006&amp;content_type=Article&amp;match_order=1&amp;q=unicode+%E7%BC%96%E7%A0%81&amp;zhida_source=entity">unicode 编码</a>，然后你在定位问题的时候使用这个关键词去搜，却发现什么都搜不到？这就扑街了。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc99380dca6f1e0489ecac968" data-id="1a1ed64fc99380dca6f1e0489ecac968"><span><div id="1a1ed64fc99380dca6f1e0489ecac968" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc99380dca6f1e0489ecac968" title="3. raw 字符串"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>3. raw 字符串</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc993809ca0d4c8bf8c94f78c">当一个字符串中具有转义的字符时，我们使用print打印后，正常情况下，输出的不是我们原来在字符串中看到的那样子。</div><div class="notion-text notion-block-1a1ed64fc99380afb6eceb849152fe60">那如果我们需要输出<code class="notion-inline-code">hello\nworld</code> ，不希望Python将<code class="notion-inline-code">\n</code>转义成换行符呢？</div><div class="notion-text notion-block-1a1ed64fc993808780ddf8e00512692b">这种情况下，你可以在定义时将字符串定义成raw字符串，只要在字符串前面加个<code class="notion-inline-code">r</code>或者<code class="notion-inline-code">R</code>即可。</div><div class="notion-text notion-block-1a1ed64fc993806da867e0dfc8d0f06c">然而，不是所有时候都可以加 <code class="notion-inline-code">r</code> 的，比如当你的字符串是由某个程序/函数返回给你的，而不是你自己生成的</div><div class="notion-text notion-block-1a1ed64fc9938033b791cfc9bba434e2">这个时候打印它，<code class="notion-inline-code">\n</code>就是换行打印。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc9938028b736cf8cefc71ac9" data-id="1a1ed64fc9938028b736cf8cefc71ac9"><span><div id="1a1ed64fc9938028b736cf8cefc71ac9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc9938028b736cf8cefc71ac9" title="4. 使用 repr"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>4. 使用 repr</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc993803cbedce055cd88fd0b">对于上面那种无法使用<code class="notion-inline-code">r</code>的情况，可以试一下<code class="notion-inline-code">repr</code>来解决这个需求：</div><div class="notion-text notion-block-1a1ed64fc9938034ba0dc48fcfecf3d0">经过<code class="notion-inline-code">repr</code>函数的处理后，为让print后的结果，接近字符串本身的样子，它实际上做了两件事</div><ol start="1" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380a89970c53611015a5c"><li>将<code class="notion-inline-code">\</code>变为了<code class="notion-inline-code">\\</code></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380e8a3bac2b366754b2a"><li>在字符串的首尾添加<code class="notion-inline-code">&#x27;</code>或者<code class="notion-inline-code">&quot;</code></li></ol><div class="notion-text notion-block-1a1ed64fc9938052aef0ea2e4dc4902f">你可以在Python Shell下敲入 变量回车，就可以能看出端倪。</div><div class="notion-text notion-block-1a1ed64fc9938045b400e8da209062eb">首尾是添加<code class="notion-inline-code">&#x27;</code>还是<code class="notion-inline-code">&quot;</code>，取决于你原字符串。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc99380749a0fe42d01e3ef2a" data-id="1a1ed64fc99380749a0fe42d01e3ef2a"><span><div id="1a1ed64fc99380749a0fe42d01e3ef2a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc99380749a0fe42d01e3ef2a" title="5. 使用 string_escape"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>5. 使用 string_escape</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc99380c1b693cdf9090e0398">如果你还在使用Python 2 ，其实还可以使用另一种方法。</div><div class="notion-text notion-block-1a1ed64fc993806ba39de213f577b02a">那就是使用<code class="notion-inline-code">string.encode(&#x27;string_escape&#x27;)</code>的方法，它同样可以达到<code class="notion-inline-code">repr</code>的效果</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc99380da8f87e1a561ea47e3" data-id="1a1ed64fc99380da8f87e1a561ea47e3"><span><div id="1a1ed64fc99380da8f87e1a561ea47e3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc99380da8f87e1a561ea47e3" title="6. 查看原生字符串"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>6. 查看原生字符串</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc9938040abe5e9761b68a52f">综上，想查看原生字符串有两种方法：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380d6bbaac3e84996afb8"><li>如果你在Python Shell交互模式下，那么敲击变量回车</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380989d34c6d82e4ad85e"><li>如果不在Python Shell交互模式下，可先使用<code class="notion-inline-code">repr</code>处理一下，再使用print打印</li></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc993800a931ef0b99f52c944" data-id="1a1ed64fc993800a931ef0b99f52c944"><span><div id="1a1ed64fc993800a931ef0b99f52c944" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993800a931ef0b99f52c944" title="7. 恢复转义：转成原字符串"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>7. 恢复转义：转成原字符串</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc99380a48a38ea13d33fbc0a">经过<code class="notion-inline-code">repr</code>处理过或者<code class="notion-inline-code">\\</code>取消转义过的字符串，有没有办法再回退出去，变成原先的有转义的字符串呢？</div><div class="notion-text notion-block-1a1ed64fc993807295f9f1adc819e8a0">答案是：有。</div><div class="notion-text notion-block-1a1ed64fc9938096b343f635d25cee04">如果你使用Python 2，可以这样：</div><div class="notion-text notion-block-1a1ed64fc99380db812cc56ee6fff9f0">如果你使用Python 3 ，可以这样：</div><div class="notion-text notion-block-1a1ed64fc99380c893ebd5768d553a0f">什么？还要区分Python 2和Python 3？太麻烦了吧。</div><div class="notion-text notion-block-1a1ed64fc99380f1a112ec4f59acf5a5">明哥教你用一种可以兼容Python 2和Python 3的写法。</div><div class="notion-text notion-block-1a1ed64fc9938045a11ddb8e48cd2888">首先是在Python 2中的输出</div><div class="notion-text notion-block-1a1ed64fc99380c9a66bcb029d1445ea">然后再看看 Python 3 中的输出</div><div class="notion-text notion-block-1a1ed64fc99380549b3dcf5572143f46">可以看到Pyhton 2中的输出 有一个<code class="notion-inline-code">u</code> ，而Python 3的输出没有了<code class="notion-inline-code">u</code>，但无论如何 ，他们都取消了转义。</div><div class="notion-text notion-block-1a1ed64fc993802bb389dab4b04f30a3">以上，就是我为大家整理的关于Python中转义的全部内容了，整理的过程，不断的发现新知识，帮助到大家的同时，自己也对转义的一些内容有了更深的理解。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a1ed64fc99381429112e360bf84d03b" data-id="1a1ed64fc99381429112e360bf84d03b"><span><div id="1a1ed64fc99381429112e360bf84d03b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc99381429112e360bf84d03b" title="🤗 总结归纳"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🤗 总结归纳</span></span></h2><ol start="1" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380f98ae8c0ab1ed88096"><li><code class="notion-inline-code">\</code>开头并接三位0-7的数值（八进制） --- 可以表示所有ASCII 字符</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380e7a84be7b747df5e7d"><li><code class="notion-inline-code">\x</code>开头并接两位0-f的数值（十六进制） --- 可以表示所有ASCII 字符</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380fdabbcd7770ee213f9"><li><code class="notion-inline-code">\u</code>开头并接四位0-f的数值（十六进制） --- 可以表示所有 Unicode 字符</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1a1ed64fc9938010a574ec86b50206bf"><li><code class="notion-inline-code">\U</code>开头并接八位0-f的数值（三十二进制）） --- 可以表示所有 Unicode 字符</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380bf9e14c66b7187035f"><li><code class="notion-inline-code">\</code>开头后接除x、u、U之外的特定字符 --- 仅可表示部分字符</li></ol><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a1ed64fc993818c99c3dfd5bc3c3c28" data-id="1a1ed64fc993818c99c3dfd5bc3c3c28"><span><div id="1a1ed64fc993818c99c3dfd5bc3c3c28" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993818c99c3dfd5bc3c3c28" title="📎 参考文章"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📎 参考文章</span></span></h2><ul class="notion-list notion-list-disc notion-block-1a1ed64fc99381139a52ecf4251b4c74"><li>一些引用</li></ul><ul class="notion-list notion-list-disc notion-block-1a1ed64fc993818698a0f13ffa888926"><li>引用文章</li></ul><div class="notion-blank notion-block-1a1ed64fc9938194ae46c34bdd6e2bdd"> </div><div class="notion-callout notion-gray_background_co notion-block-1a1ed64fc99381acbb2addaec85182cc"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[详解Python的生成器与协程]]></title>
            <link>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a1ed64f-c993-808c-bda8-f1f8d9072138</link>
            <guid>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a1ed64f-c993-808c-bda8-f1f8d9072138</guid>
            <pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[使用isinstance()来类别一个对象是否是可迭代的（Iterable），是否是迭代器（Iterator），是否是生成器（Generator）。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1a1ed64fc993808cbda8f1f8d9072138"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-1a1ed64fc993811d9dddd2f5c3baa95a"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">从今天开始，我们将开始进入Python的难点，那就是<code class="notion-inline-code"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147664779&amp;content_type=Article&amp;match_order=1&amp;q=%E5%8D%8F%E7%A8%8B&amp;zhida_source=entity">协程</a></code>。<div class="notion-text notion-block-1a1ed64fc9938021a825f31247df6ce6">为了写明白协程的知识点，我查阅了网上的很多相关资料。发现很难有一个讲得系统，讲得全面的文章，导致我们在学习的时候，往往半知半解，学完还是一脸懵逼。</div><div class="notion-text notion-block-1a1ed64fc99380c89ec4e1687271a692">学习协程的第一门课程，是要认识<code class="notion-inline-code">生成器</code>，有了<code class="notion-inline-code">生成器</code>的基础，才能更好地理解<code class="notion-inline-code">协程</code>。</div><div class="notion-text notion-block-1a1ed64fc9938079bb82e726ceba5414">如果你是新手，那么你应该知道<code class="notion-inline-code">迭代器</code>，对<code class="notion-inline-code">生成器</code>应该是比较陌生的吧。没关系，看完这系列文章，你也能从小白成功过渡为Ptyhon高手。</div></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a1ed64fc993810cb6e7f90225955883" data-id="1a1ed64fc993810cb6e7f90225955883"><span><div id="1a1ed64fc993810cb6e7f90225955883" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993810cb6e7f90225955883" title="📝 主旨内容"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📝 主旨内容</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc9938088bf72c04a02c8c35c" data-id="1a1ed64fc9938088bf72c04a02c8c35c"><span><div id="1a1ed64fc9938088bf72c04a02c8c35c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc9938088bf72c04a02c8c35c" title="1. 可迭代、迭代器、生成器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>1. 可迭代、迭代器、生成器</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc99380f3843de4e330cf8f75">初学Python的时候，对于这三货真的是傻傻分不清。甚至还认为他们是等价的。</div><div class="notion-text notion-block-1a1ed64fc9938047be9afa01b8f5e44d">其实，他们是不一样的。</div><div class="notion-text notion-block-1a1ed64fc9938071958ff554a71fb094">可迭代的对象，很好理解，我们很熟悉的：<code class="notion-inline-code">字符串</code>，<code class="notion-inline-code">list</code>，<code class="notion-inline-code">dict</code>，<code class="notion-inline-code">tuple</code>，<code class="notion-inline-code">deque</code>等</div><div class="notion-text notion-block-1a1ed64fc9938063b79ff79c5b0900f5">为了验证我说的，需要借助<code class="notion-inline-code">collections.abc</code>这个模块（Python2没有），使用<code class="notion-inline-code">isinstance()</code>来类别一个对象是否是可迭代的（<code class="notion-inline-code">Iterable</code>），是否是迭代器（<code class="notion-inline-code">Iterator</code>），是否是生成器（<code class="notion-inline-code">Generator</code>）。</div><div class="notion-text notion-block-1a1ed64fc993806d93a5cfb073c1c2f5">这几个判断方法，在这里适用，但并不是绝对适用，原因见后面补充说明。</div><div class="notion-text notion-block-1a1ed64fc99380b7992ae76cf3c1abbe">输出结果</div><div class="notion-text notion-block-1a1ed64fc993804db5d8c4a846b36c71">从结果来看，这些可迭代对象都不是迭代器，也不是生成器。它们有一个共同点，就是它们都可以使用<code class="notion-inline-code">for</code>来循环。这一点，大家都知道，我们就不去验证了。</div><div class="notion-text notion-block-1a1ed64fc993803080cdf7127ec1f934"><b>关于可迭代对象，有几点需要补充说明</b></div><ol start="1" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380048ee6dd9783deb04c"><li>可以通过，<code class="notion-inline-code">dir()</code>方法查看，若有有<code class="notion-inline-code">__iter__</code>说明是可迭代的，但是如果没有，也不能说明不可迭代，原因见第二条。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a1ed64fc9938054b5c1e56e38947651"><li>判断是否可迭代，不能仅看是否有<code class="notion-inline-code">__iter__</code> 来草率决定，因为只实现了<code class="notion-inline-code">__getitem__</code> 方法的也有可能是可迭代的。因为当没有<code class="notion-inline-code">__iter__</code>时， Python 解释器会去找<code class="notion-inline-code">__getitem__</code>，尝试按顺序（从索引0开始）获取元素，不抛异常，即是可迭代。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380e59f92e04e72669a5d"><li>所以，最好的判断方法应该是通过 <code class="notion-inline-code"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147664779&amp;content_type=Article&amp;match_order=1&amp;q=for%E5%BE%AA%E7%8E%AF&amp;zhida_source=entity">for循环</a></code>或者<code class="notion-inline-code">iter()</code> 去真实运行。</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1a1ed64fc9938092ba16d0e80202fa03"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:300px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="attachment:d85de767-b9e8-46dc-b013-b561d0686b78:QQ20250221-140353.png?t=1a1ed64f-c993-8092-ba16-d0e80202fa03" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1a1ed64fc9938070b7c6f64493fcaecf">接下来是，<code class="notion-inline-code">迭代器</code>。 对比可迭代对象，<code class="notion-inline-code">迭代器</code>其实就只是多了一个函数而已。就是<code class="notion-inline-code">__next__()</code>，我们可以不再使用<code class="notion-inline-code">for</code>循环来间断获取元素值。而可以直接使用next()方法来实现。</div><div class="notion-text notion-block-1a1ed64fc99380ba948ee203863ef785">迭代器，是在可迭代的基础上实现的。要创建一个迭代器，我们首先，得有一个可迭代对象。 现在就来看看，如何创建一个可迭代对象，并以可迭代对象为基础创建一个迭代器。</div><div class="notion-text notion-block-1a1ed64fc99380608602dc15354b63ed">输出</div><div class="notion-text notion-block-1a1ed64fc99380a4b073e972ea6fe07d">如果上面的代码太多，也可以看这边，你更能理解。</div><div class="notion-text notion-block-1a1ed64fc993805bb704fae1b9cbc92c"><b>补充说明</b>:</div><ol start="1" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380f5ad61c6628ca6e3a1"><li>迭代器，是其内部实现了，<code class="notion-inline-code">__next__</code>这个<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147664779&amp;content_type=Article&amp;match_order=1&amp;q=%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95&amp;zhida_source=entity">魔术方法</a>。(Python3.x)</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a1ed64fc993809f9714db6719eec44e"><li>可以通过，<code class="notion-inline-code">dir()</code>方法来查看是否有<code class="notion-inline-code">__next__</code>来判断一个变量是否是迭代器的。</li></ol><div class="notion-text notion-block-1a1ed64fc99380c0ba2aecd763249a59">接下来，是我们的重点，<code class="notion-inline-code">生成器</code>。</div><div class="notion-text notion-block-1a1ed64fc993809d84acf80ab6d718a9">生成器的概念在 Python 2.2 中首次出现，之所以引入生成器，是为了实现一个在计算下一个值时不需要浪费空间的结构。</div><div class="notion-text notion-block-1a1ed64fc99380ffaf7debcac83a5d61">前面我们说，迭代器，是在可迭代的基础上，加了一个next()方法。 而生成器，则是在迭代器的基础上（<code class="notion-inline-code">可以用for循环，可以使用next()</code>），再实现了<code class="notion-inline-code">yield</code>。</div><div class="notion-text notion-block-1a1ed64fc993802ba43be20abae22c80"><code class="notion-inline-code">yield</code>是什么东西呢，它相当于我们函数里的return。在每次next()，或者for遍历的时候，都会yield这里将新的值返回回去，并在这里阻塞，等待下一次的调用。正是由于这个机制，才使用生成器在Python编程中大放异彩。实现节省内存，实现<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147664779&amp;content_type=Article&amp;match_order=1&amp;q=%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B&amp;zhida_source=entity">异步编程</a>。</div><div class="notion-text notion-block-1a1ed64fc99380ec8f7ee803345b8342">如何创建一个生成器，主要有如下两种方法</div><ul class="notion-list notion-list-disc notion-block-1a1ed64fc99380f6b8abdbaefd51de49"><li>使用列表生成式</li></ul><div class="notion-text notion-block-1a1ed64fc993808a8bb6f9171ef121f2">实现yield的函数</div><div class="notion-text notion-block-1a1ed64fc9938073a06bf7b627c50d78">可迭代对象和迭代器，是将所有的值都生成存放在内存中，而<code class="notion-inline-code">生成器</code>则是需要元素才临时生成，节省时间，节省空间。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc993805db561d9e80534b1bb" data-id="1a1ed64fc993805db561d9e80534b1bb"><span><div id="1a1ed64fc993805db561d9e80534b1bb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993805db561d9e80534b1bb" title="2. 如何运行/激活生成器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2. 如何运行/激活生成器</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc993806ab128f2dc8d78e3d6">由于生成器并不是一次生成所有元素，而是一次一次的执行返回，那么如何刺激生成器执行(或者说激活)呢？</div><div class="notion-text notion-block-1a1ed64fc993804fbb6fe87a9992703b">激活主要有两个方法</div><ul class="notion-list notion-list-disc notion-block-1a1ed64fc99380d5802adfab11ffabe2"><li>使用<code class="notion-inline-code">next()</code></li></ul><ul class="notion-list notion-list-disc notion-block-1a1ed64fc9938019ade0d74ae6df6f8a"><li>使用<code class="notion-inline-code">generator.send(None)</code></li></ul><div class="notion-text notion-block-1a1ed64fc9938026a8cbfdf71d621554">分别看下例子，你就知道了。</div><div class="notion-text notion-block-1a1ed64fc99380688799e89ef9b4818a">输出</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc993801b810ff5db585d2ad3" data-id="1a1ed64fc993801b810ff5db585d2ad3"><span><div id="1a1ed64fc993801b810ff5db585d2ad3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993801b810ff5db585d2ad3" title="3. 生成器的执行状态"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>3. 生成器的执行状态</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc9938042a13ccac4e16e5f33">生成器在其生命周期中，会有如下四个状态</div><blockquote class="notion-quote notion-block-1a1ed64fc9938051880cc54117387e71"><div>GEN_CREATED # 等待开始执行 GEN_RUNNING # 解释器正在执行（只有在多线程应用中才能看到这个状态） GEN_SUSPENDED # 在yield表达式处暂停 GEN_CLOSED # 执行结束</div></blockquote><div class="notion-text notion-block-1a1ed64fc993801090a1f2f13554567c">通过代码来感受一下，为了不增加代码理解难度，<code class="notion-inline-code">GEN_RUNNING</code>这个状态，我就不举例了。有兴趣的同学，可以去尝试一下多线程。若有疑问，可在后台回复我。</div><div class="notion-text notion-block-1a1ed64fc993802f89e1ce73eeef2ee6">输出</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc99380508ce1c81476d0554a" data-id="1a1ed64fc99380508ce1c81476d0554a"><span><div id="1a1ed64fc99380508ce1c81476d0554a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc99380508ce1c81476d0554a" title="4. 生成器的异常处理"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>4. 生成器的异常处理</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc9938018a163f2692ec24abd">在生成器工作过程中，若生成器不满足生成元素的条件，就<code class="notion-inline-code">会</code>/<code class="notion-inline-code">应该</code> 抛出异常（<code class="notion-inline-code">StopIteration</code>）。</div><div class="notion-text notion-block-1a1ed64fc9938064bd11e8f90240ef34">通过列表生成式构建的生成器，其内部已经自动帮我们实现了抛出异常这一步。不信我们来看一下。</div><div class="notion-text notion-block-1a1ed64fc993803480dbd13fcf7ab3a5">所以我们在自己定义一个生成器的时候，我们也应该在不满足生成元素条件的时候，抛出异常。 拿上面的代码来修改一下。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc993803f8741ce30a97e72d2" data-id="1a1ed64fc993803f8741ce30a97e72d2"><span><div id="1a1ed64fc993803f8741ce30a97e72d2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993803f8741ce30a97e72d2" title="5. 从生成器过渡到协程：yield"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>5. 从生成器过渡到协程：yield</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc993807a8991c662dd87fef3">通过上面的介绍，我们知道生成器为我们引入了暂停函数执行（<code class="notion-inline-code">yield</code>）的功能。当有了暂停的功能之后，人们就想能不能在生成器暂停的时候向其发送一点东西（其实上面也有提及：<code class="notion-inline-code">send(None)</code>）。这种向暂停的生成器发送信息的功能通过 <code class="notion-inline-code">PEP 342</code>进入<code class="notion-inline-code">Python 2.5</code> 中，并催生了<code class="notion-inline-code">Python</code>中<code class="notion-inline-code">协程</code>的诞生。根据<code class="notion-inline-code">wikipedia</code>中的定义</div><blockquote class="notion-quote notion-block-1a1ed64fc993802dae70fa3e6add2e98"><div>协程是为非抢占式多任务产生子程序的计算机程序组件，协程允许不同入口点在不同位置暂停或开始执行程序。</div></blockquote><div class="notion-text notion-block-1a1ed64fc9938047a21de2168b8fffbc">注意从本质上而言，协程并不属于语言中的概念，而是编程模型上的概念。</div><div class="notion-text notion-block-1a1ed64fc9938000acafec4d605eeadd">协程和线程，有<code class="notion-inline-code">相似点</code>，多个协程之间和线程一样，只会交叉串行执行；也有<code class="notion-inline-code">不同点</code>，线程之间要频繁进行切换，加锁，解锁，从复杂度和效率来看，和协程相比，这确是一个痛点。协程通过使用<code class="notion-inline-code">yield</code>暂停生成器，可以将程序的执行流程交给其他的子程序，从而实现不同子程序的之间的交替执行。</div><div class="notion-text notion-block-1a1ed64fc99380e0a6f7fca2f1fad49d">下面通过一个简明的演示来看看，如何向生成器中发送消息。</div><div class="notion-text notion-block-1a1ed64fc9938095945fdd502a7f7f7b">输出。</div><div class="notion-text notion-block-1a1ed64fc993807b820ff3777092b0ec">这里解释下为什么这么输出。重点是<code class="notion-inline-code">jump = yield index</code>这个语句。</div><div class="notion-text notion-block-1a1ed64fc99380a5ab39f7a8ba85ccfc">分成两部分：</div><ul class="notion-list notion-list-disc notion-block-1a1ed64fc99380c6bcf3cafe35851c6e"><li><code class="notion-inline-code">yield index</code>是将index<code class="notion-inline-code">return</code>给外部调用程序。</li></ul><ul class="notion-list notion-list-disc notion-block-1a1ed64fc99380d9ad53f05b0c97a311"><li><code class="notion-inline-code">jump = yield</code>可以接收外部程序通过send()发送的信息，并赋值给<code class="notion-inline-code">jump</code></li></ul><div class="notion-text notion-block-1a1ed64fc9938083af0aea231eb89397">以上这些，都是讲协程并发的<b>基础必备知识</b>，<b>请一定要亲自去实践并理解它</b>，不然后面的内容，将会变得枯燥无味，晦涩难懂。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a1ed64fc993817f943ee5e849d1674e" data-id="1a1ed64fc993817f943ee5e849d1674e"><span><div id="1a1ed64fc993817f943ee5e849d1674e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993817f943ee5e849d1674e" title="🤗 总结归纳"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🤗 总结归纳</span></span></h2><div class="notion-text notion-block-1a1ed64fc9938107afbee70695c17a11">总结文章的内容</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a1ed64fc993815cae36fc66317bb90d" data-id="1a1ed64fc993815cae36fc66317bb90d"><span><div id="1a1ed64fc993815cae36fc66317bb90d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993815cae36fc66317bb90d" title="📎 参考文章"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📎 参考文章</span></span></h2><ul class="notion-list notion-list-disc notion-block-1a1ed64fc99381978c25ca0b96c3d96a"><li>一些引用</li></ul><ul class="notion-list notion-list-disc notion-block-1a1ed64fc99381f7bfbdc75e337e25df"><li>引用文章</li></ul><div class="notion-blank notion-block-1a1ed64fc99381f7962afab439e4b67e"> </div><div class="notion-callout notion-gray_background_co notion-block-1a1ed64fc9938164818ef3b6c10f582c"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[深入理解Python中的yield from语法]]></title>
            <link>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a1ed64f-c993-8086-aa26-fb0ad89336a3</link>
            <guid>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a1ed64f-c993-8086-aa26-fb0ad89336a3</guid>
            <pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[利用常规的方法，几乎是没办法实现如上我们想要的效果的。所以Python想得很周到，从语言本身给我们实现了这样的功能，这就是yield语法。可以实现在某一函数中暂停的效果。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1a1ed64fc9938086aa26fb0ad89336a3"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-1a1ed64fc99381478209c301d911330d"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">我们都知道，<code class="notion-inline-code">get_html()</code>等待返回网页是非常耗IO的，一个网页还好，如果我们爬取的网页数据极其庞大，这个等待时间就非常惊人，是极大的浪费。<div class="notion-text notion-block-1a1ed64fc99380ffb394f714aaa761bd">聪明的程序员，当然会想如果能在<code class="notion-inline-code">get_html()</code>这里暂停一下，不用傻乎乎地去等待网页返回，而是去做别的事。等过段时间再回过头来到刚刚暂停的地方，接收返回的html内容，然后还可以接下去解析<code class="notion-inline-code">parse_html(html)</code>。</div><div class="notion-text notion-block-1a1ed64fc9938064b1dae91353d44056">利用常规的方法，几乎是没办法实现如上我们想要的效果的。所以Python想得很周到，从语言本身给我们实现了这样的功能，这就是<code class="notion-inline-code">yield</code>语法。可以实现在某一函数中暂停的效果。</div></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a1ed64fc99381c5be1df34391fd405e" data-id="1a1ed64fc99381c5be1df34391fd405e"><span><div id="1a1ed64fc99381c5be1df34391fd405e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc99381c5be1df34391fd405e" title="📝 主旨内容"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📝 主旨内容</span></span></h2><blockquote class="notion-quote notion-block-1a1ed64fc993809c88e8fbb9614d807d"><div>友情提醒： 本系列所有的代码均在Python3下编写。Python2中可能有所差异。</div></blockquote><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc99380adbeadf5d5206449ae" data-id="1a1ed64fc99380adbeadf5d5206449ae"><span><div id="1a1ed64fc99380adbeadf5d5206449ae" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc99380adbeadf5d5206449ae" title="1. 为什么要使用协程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>1. 为什么要使用协程</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc9938056ae11f0056a7926b9">在上一篇中，我们从生成器的基本认识与使用，成功过渡到了协程。</div><div class="notion-text notion-block-1a1ed64fc99380899789db83982cc986">但一定有许多人，只知道协程是个什么东西，但并不知道为什么要用协程？换句话来说，并不知道在什么情况下用协程？ 它相比多线程来说，有哪些过人之处呢？</div><div class="notion-text notion-block-1a1ed64fc9938084bf49ce0c648b1d6b">在开始讲<code class="notion-inline-code"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147678405&amp;content_type=Article&amp;match_order=1&amp;q=yield+from&amp;zhida_source=entity">yield from</a></code>之前，我想先解决一下这个给很多人带来困惑的问题。</div><div class="notion-text notion-block-1a1ed64fc99380fb83afca3f80b8c6cc">举个例子。 假如我们做一个爬虫。我们要爬取多个网页，这里简单举例两个网页(两个spider函数)，获取HTML（耗IO耗时），然后再对HTML对行解析取得我们感兴趣的数据。</div><div class="notion-text notion-block-1a1ed64fc99380afbfebf1b48bc08b2d">我们的代码结构精简如下：</div><div class="notion-text notion-block-1a1ed64fc9938045839ac3e79c2c6b49">我们都知道，<code class="notion-inline-code">get_html()</code>等待返回网页是非常耗IO的，一个网页还好，如果我们爬取的网页数据极其庞大，这个等待时间就非常惊人，是极大的浪费。</div><div class="notion-text notion-block-1a1ed64fc99380c486f3db4a92c45147">聪明的程序员，当然会想如果能在<code class="notion-inline-code">get_html()</code>这里暂停一下，不用傻乎乎地去等待网页返回，而是去做别的事。等过段时间再回过头来到刚刚暂停的地方，接收返回的html内容，然后还可以接下去解析<code class="notion-inline-code">parse_html(html)</code>。</div><div class="notion-text notion-block-1a1ed64fc993803bb200fffb70c4d865">利用常规的方法，几乎是没办法实现如上我们想要的效果的。所以Python想得很周到，从语言本身给我们实现了这样的功能，这就是<code class="notion-inline-code">yield</code>语法。可以实现在某一函数中暂停的效果。</div><div class="notion-text notion-block-1a1ed64fc99380ceabffdf268aff51a0">试着思考一下，假如没有协程，我们要写一个并发程序。可能有以下问题</div><ol start="1" class="notion-list notion-list-numbered notion-block-1a1ed64fc9938058b56be0eded68adba"><li>使用最常规的同步编程要实现异步并发效果并不理想，或者难度极高。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380038120f57401d615d3"><li>由于<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147678405&amp;content_type=Article&amp;match_order=1&amp;q=GIL%E9%94%81&amp;zhida_source=entity">GIL锁</a>的存在，多线程的运行需要频繁的加锁解锁，切换线程，这极大地降低了并发性能；</li></ol><div class="notion-text notion-block-1a1ed64fc993800599c0d05de63da237">而协程的出现，刚好可以解决以上的问题。它的特点有</div><ol start="1" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380b7a53ac3acbf517f8d"><li>协程是在单线程里实现任务的切换的</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a1ed64fc9938053aa28c79ec6196009"><li>利用同步的方式去实现异步</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380bfbe0ac4872178e0bb"><li>不再需要锁，提高了并发性能</li></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc993805fa32acd1910db1d9e" data-id="1a1ed64fc993805fa32acd1910db1d9e"><span><div id="1a1ed64fc993805fa32acd1910db1d9e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993805fa32acd1910db1d9e" title="2. yield from的用法详解"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2. yield from的用法详解</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc99380a6b4c4c7b121b40eb4"><code class="notion-inline-code">yield from</code> 是在Python3.3才出现的语法。所以这个特性在Python2中是没有的。</div><div class="notion-text notion-block-1a1ed64fc99380699ad1f2c564c53726"><code class="notion-inline-code">yield from</code> 后面需要加的是可迭代对象，它可以是普通的可迭代对象，也可以是<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147678405&amp;content_type=Article&amp;match_order=1&amp;q=%E8%BF%AD%E4%BB%A3%E5%99%A8&amp;zhida_source=entity">迭代器</a>，甚至是生成器。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1a1ed64fc9938029bafafd798687849f" data-id="1a1ed64fc9938029bafafd798687849f"><span><div id="1a1ed64fc9938029bafafd798687849f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc9938029bafafd798687849f" title="2.1 简单应用：拼接可迭代对象"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2.1 简单应用：拼接可迭代对象</b></span></span></h4><div class="notion-text notion-block-1a1ed64fc993802b9d79d75e0cfc7971">我们可以用一个使用<code class="notion-inline-code">yield</code>和一个使用<code class="notion-inline-code">yield from</code>的例子来对比看下。</div><div class="notion-text notion-block-1a1ed64fc993809cbffee11f915c4d99">使用<code class="notion-inline-code">yield</code></div><div class="notion-text notion-block-1a1ed64fc9938011a1b3e467c18b47ba">使用<code class="notion-inline-code">yield from</code></div><div class="notion-text notion-block-1a1ed64fc99380ad9658d11ee7584c36">由上面两种方式对比，可以看出，yield from后面加上可迭代对象，他可以把可迭代对象里的每个元素一个一个的yield出来，对比yield来说代码更加简洁，结构更加清晰。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1a1ed64fc993806885f3dd503bd54902" data-id="1a1ed64fc993806885f3dd503bd54902"><span><div id="1a1ed64fc993806885f3dd503bd54902" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993806885f3dd503bd54902" title="2.2 复杂应用：生成器的嵌套"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2.2 复杂应用：生成器的嵌套</b></span></span></h4><div class="notion-text notion-block-1a1ed64fc99380eea11ec238e196738e">如果你认为只是 <code class="notion-inline-code">yield from</code> 仅仅只有上述的功能的话，那你就太小瞧了它，它的更强大的功能还在后面。</div><div class="notion-text notion-block-1a1ed64fc99380beb07afc9f130825ec">当 <code class="notion-inline-code">yield from</code> 后面加上一个生成器后，就实现了生成的嵌套。</div><div class="notion-text notion-block-1a1ed64fc993801eb9a4f0a9794aa642">当然实现生成器的嵌套，并不是一定必须要使用<code class="notion-inline-code">yield from</code>，而是使用<code class="notion-inline-code">yield from</code>可以让我们避免让我们自己处理各种料想不到的异常，而让我们专注于业务代码的实现。</div><div class="notion-text notion-block-1a1ed64fc99380a6b8cfe8a6dabb09ef">如果自己用<code class="notion-inline-code">yield</code>去实现，那只会加大代码的编写难度，降低开发效率，降低代码的可读性。既然Python已经想得这么周到，我们当然要好好利用起来。</div><div class="notion-text notion-block-1a1ed64fc993804a9101d3de75b846bf">讲解它之前，首先要知道这个几个概念</div><blockquote class="notion-quote notion-block-1a1ed64fc993801380ace6ef9596ff82"><div>1、调用方：调用委派生成器的客户端（调用方）代码 2、委托生成器：包含yield from表达式的生成器函数 3、子生成器：yield from后面加的生成器函数</div></blockquote><div class="notion-text notion-block-1a1ed64fc993808fb196c2ce3c7be387">你可能不知道他们都是什么意思，没关系，来看下这个例子。</div><div class="notion-text notion-block-1a1ed64fc993800380f9c94b2f8d487b">这个例子，是实现实时计算平均值的。 比如，第一次传入10，那返回平均数自然是10. 第二次传入20，那返回平均数是(10+20)/2=15 第三次传入30，那返回平均数(10+20+30)/3=20</div><div class="notion-text notion-block-1a1ed64fc9938073ab4bde87861b9c85">认真阅读以上代码，你应该很容易能理解，调用方、委托生成器、子生成器之间的关系。我就不多说了</div><div class="notion-text notion-block-1a1ed64fc9938081a9b1e9c060df240f"><b>委托生成器的作用是</b>：在调用方与子生成器之间建立一个<code class="notion-inline-code">双向通道</code>。</div><div class="notion-text notion-block-1a1ed64fc99380aa83a4ebee024261c1">所谓的双向通道是什么意思呢？ 调用方可以通过<code class="notion-inline-code">send()</code>直接发送消息给子生成器，而子生成器yield的值，也是直接返回给调用方。</div><div class="notion-text notion-block-1a1ed64fc993805aa26bf1fded145fd5">你可能会经常看到有些代码，还可以在<code class="notion-inline-code">yield from</code>前面看到可以赋值。这是什么用法？</div><div class="notion-text notion-block-1a1ed64fc99380818382c25c7db92fdc">你可能会以为，子生成器yield回来的值，被委托生成器给拦截了。你可以亲自写个demo运行试验一下，并不是你想的那样。 因为我们之前说了，委托生成器，只起一个桥梁作用，它建立的是一个<code class="notion-inline-code">双向通道</code>，它并没有权利也没有办法，对子生成器yield回来的内容做拦截。</div><div class="notion-text notion-block-1a1ed64fc993807391e4e0109cb43870">为了解释这个用法，我还是用上述的例子，并对其进行了一些改造。添加了一些注释，希望你能看得明白。</div><div class="notion-text notion-block-1a1ed64fc9938088a118e1304e0fec1a">按照惯例，我们还是举个例子。</div><div class="notion-text notion-block-1a1ed64fc993804e8a6adc185a68f1d9">运行后，输出</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a1ed64fc993804c8484d0c8f776d1f4" data-id="1a1ed64fc993804c8484d0c8f776d1f4"><span><div id="1a1ed64fc993804c8484d0c8f776d1f4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993804c8484d0c8f776d1f4" title="3. 为什么要使用yield from"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>3. 为什么要使用yield from</b></span></span></h3><div class="notion-text notion-block-1a1ed64fc99380daaa0fc93e3ecff756">学到这里，我相信你肯定要问，既然委托生成器，起到的只是一个双向通道的作用，我还需要委托生成器做什么？我调用方直接调用子生成器不就好啦？</div><div class="notion-text notion-block-1a1ed64fc993804eb15fdd37e62a25bb">高能预警~</div><div class="notion-text notion-block-1a1ed64fc993802a8d1ffbece21fcb22">下面我们来一起探讨一下，到底yield from 有什么过人之处，让我们非要用它不可。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1a1ed64fc993808b8537f487203b9ab8" data-id="1a1ed64fc993808b8537f487203b9ab8"><span><div id="1a1ed64fc993808b8537f487203b9ab8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993808b8537f487203b9ab8" title="3.1 因为它可以帮我们处理异常"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>3.1 因为它可以帮我们处理异常</b></span></span></h4><div class="notion-text notion-block-1a1ed64fc9938021a4f5d4afafc50c30">如果我们去掉委托生成器，而直接调用子生成器。那我们就需要把代码改成像下面这样，我们需要自己捕获异常并处理。而不像使<code class="notion-inline-code">yield from</code>那样省心。</div><div class="notion-text notion-block-1a1ed64fc993806e9630fb973795cdad">此时的你，可能会说，不就一个<code class="notion-inline-code">StopIteration</code>的异常吗？自己捕获也没什么大不了的。</div><div class="notion-text notion-block-1a1ed64fc9938099932edeb724e7ea33">你要是知道<code class="notion-inline-code">yield from</code>在背后为我们默默无闻地做了哪些事，你就不会这样说了。</div><div class="notion-text notion-block-1a1ed64fc99380389b19f73c591a32cf">具体<code class="notion-inline-code">yield from</code>为我们做了哪些事，可以参考如下这段代码。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a1ed64fc99381b0a2ceebfcbbbd34fa" data-id="1a1ed64fc99381b0a2ceebfcbbbd34fa"><span><div id="1a1ed64fc99381b0a2ceebfcbbbd34fa" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc99381b0a2ceebfcbbbd34fa" title="🤗 总结归纳"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🤗 总结归纳</span></span></h2><div class="notion-text notion-block-1a1ed64fc99380fbba18cf27510a6eea">以上的代码，稍微有点复杂，有兴趣的同学可以结合以下说明去研究看看。</div><ol start="1" class="notion-list notion-list-numbered notion-block-1a1ed64fc9938063bcbcef6fe60bb836"><li>迭代器（即可指子生成器）产生的值直接返还给调用者</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a1ed64fc993800bb8dbcf53953b1a91"><li>任何使用send()方法发给委派生产器（即外部生产器）的值被直接传递给迭代器。如果send值是None，则调用迭代器next()方法；如果不为None，则调用迭代器的send()方法。如果对迭代器的调用产生StopIteration异常，委派生产器恢复继续执行yield from后面的语句；若迭代器产生其他任何异常，则都传递给委派生产器。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1a1ed64fc9938011822de662b6882add"><li>子生成器可能只是一个迭代器，并不是一个作为协程的生成器，所以它不支持.throw()和.close()方法,即可能会产生AttributeError 异常。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380338ab9c6982e83ee68"><li>除了GeneratorExit 异常外的其他抛给委派生产器的异常，将会被传递到迭代器的throw()方法。如果迭代器throw()调用产生了StopIteration异常，委派生产器恢复并继续执行，其他异常则传递给委派生产器。</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1a1ed64fc9938079b45ae1f07f442f53"><li>如果GeneratorExit异常被抛给委派生产器，或者委派生产器的close()方法被调用，如果迭代器有close()的话也将被调用。如果close()调用产生异常，异常将传递给委派生产器。否则，委派生产器将抛出GeneratorExit 异常。</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380048500ea43c8d745bb"><li>当迭代器结束并抛出异常时，yield from表达式的值是其StopIteration异常中的第一个参数。</li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-1a1ed64fc99380778b37ca46dcec4642"><li>一个生成器中的return expr语句将会从生成器退出并抛出 StopIteration(expr)异常。</li></ol><div class="notion-text notion-block-1a1ed64fc9938072915ad3d479879827">没兴趣看的同学，只要知道，<code class="notion-inline-code">yield from</code>帮我们做了很多的异常处理，而且全面，而这些如果我们要自己去实现的话，一个是编写代码难度增加，写出来的代码可读性极差，这些我们就不说了，最主要的是很可能有遗漏，只要哪个异常没考虑到，都有可能导致程序崩溃什么的。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a1ed64fc993812daf9ee0424f3ba1c5" data-id="1a1ed64fc993812daf9ee0424f3ba1c5"><span><div id="1a1ed64fc993812daf9ee0424f3ba1c5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a1ed64fc993812daf9ee0424f3ba1c5" title="📎 参考文章"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📎 参考文章</span></span></h2><ul class="notion-list notion-list-disc notion-block-1a1ed64fc9938196818af192903d580a"><li>一些引用</li></ul><ul class="notion-list notion-list-disc notion-block-1a1ed64fc993811090d0f5a0a415c7b8"><li>引用文章</li></ul><div class="notion-blank notion-block-1a1ed64fc9938171af7dff855aa0f481"> </div><div class="notion-callout notion-gray_background_co notion-block-1a1ed64fc993817db257f5f84d6ce4c3"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Python并发编程（四）：详解 Python线程消息通信机制]]></title>
            <link>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a0ed64f-c993-809a-a3c0-cb4ce6802fb8</link>
            <guid>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a0ed64f-c993-809a-a3c0-cb4ce6802fb8</guid>
            <pubDate>Thu, 20 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Event和Condition是threading模块原生提供的模块，原理简单，功能单一，它能发送True和False的指令，所以只能适用于某些简单的场景中。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1a0ed64fc993809aa3c0cb4ce6802fb8"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-1a0ed64fc993810da60ce5189736a7d9"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">这里写文章的前言：
一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么？并简述你给出的答案。<div class="notion-text notion-block-1a0ed64fc9938199955dce6aaf8531f5">可以说说你的故事：阻碍、努力、结果成果，意外与转折。</div></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a0ed64fc99381d5a687d5f9747a3d47" data-id="1a0ed64fc99381d5a687d5f9747a3d47"><span><div id="1a0ed64fc99381d5a687d5f9747a3d47" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc99381d5a687d5f9747a3d47" title="📝 主旨内容"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📝 主旨内容</span></span></h2><div class="notion-text notion-block-1a0ed64fc99380c08128dd3fbadd189a">前面我已经向大家介绍了，如何使用创建线程，启动线程。相信大家都会有这样一个想法，线程无非就是创建一下，然后再<code class="notion-inline-code">start()</code>下，实在是太简单了。</div><div class="notion-text notion-block-1a0ed64fc99380d0ae79eea4e0dcabbf">可是要知道，在真实的项目中，实际场景可要我们举的例子要复杂的多得多，不同线程的执行可能是有顺序的，或者说他们的执行是有条件的，是要受控制的。如果仅仅依靠前面学的那点浅薄的知识，是远远不够的。</div><div class="notion-text notion-block-1a0ed64fc993803aaddfe8355372ddd4">那今天，我们就来探讨一下如何控制线程的触发执行。</div><div class="notion-text notion-block-1a0ed64fc993809eb926d688fa227860">要实现对多个线程进行控制，其实本质上就是消息通信机制在起作用，利用这个机制发送指令，告诉线程，什么时候可以执行，什么时候不可以执行，执行什么内容。</div><div class="notion-text notion-block-1a0ed64fc9938045862efbdc78262864">经过我的总结，线程中通信方法大致有如下三种：</div><ul class="notion-list notion-list-disc notion-block-1a0ed64fc99380879c62ccf304581aa3"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147628345&amp;content_type=Article&amp;match_order=1&amp;q=threading.Event&amp;zhida_source=entity">threading.Event</a></li></ul><ul class="notion-list notion-list-disc notion-block-1a0ed64fc993807496d0ff3200574993"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147628345&amp;content_type=Article&amp;match_order=1&amp;q=threading.Condition&amp;zhida_source=entity">threading.Condition</a></li></ul><ul class="notion-list notion-list-disc notion-block-1a0ed64fc9938036be15c2f56ccd8bc7"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147628345&amp;content_type=Article&amp;match_order=1&amp;q=queue.Queue&amp;zhida_source=entity">queue.Queue</a></li></ul><div class="notion-text notion-block-1a0ed64fc99380398e31fcaa21f76f90">接下来我们来一一探讨下。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a0ed64fc99380e1aed2f52735a4e639" data-id="1a0ed64fc99380e1aed2f52735a4e639"><span><div id="1a0ed64fc99380e1aed2f52735a4e639" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc99380e1aed2f52735a4e639" title="2.4.1 Event事件"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2.4.1 Event事件</b></span></span></h3><div class="notion-text notion-block-1a0ed64fc9938008b955e85f6148238a">Python提供了非常简单的通信机制 <code class="notion-inline-code">Threading.Event</code>，通用的条件变量。多个线程可以<code class="notion-inline-code">等待某个事件的发生</code>，在事件发生后，<code class="notion-inline-code">所有的线程</code>都会被<code class="notion-inline-code">激活</code>。</div><div class="notion-text notion-block-1a0ed64fc99380b4ad15d8812102f1c4">关于Event的使用也超级简单，就三个函数</div><div class="notion-text notion-block-1a0ed64fc99380a59263ee58d6b1cfbe">举个例子来看下。</div><div class="notion-text notion-block-1a0ed64fc9938037b708d62235dd490b">执行一下，看看结果</div><div class="notion-text notion-block-1a0ed64fc99380bdac31e25c667544ae">可见在所有线程都启动（<code class="notion-inline-code">start()</code>）后，并不会执行完，而是都在<code class="notion-inline-code">self.event.wait()</code>止住了，需要我们通过<code class="notion-inline-code">event.set()</code>来给所有线程发送执行指令才能往下执行。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a0ed64fc99380119b3fd4dc7221d004" data-id="1a0ed64fc99380119b3fd4dc7221d004"><span><div id="1a0ed64fc99380119b3fd4dc7221d004" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc99380119b3fd4dc7221d004" title="2.4.2 Condition"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2.4.2 Condition</b></span></span></h3><div class="notion-text notion-block-1a0ed64fc99380779f04fd7fa7604627">Condition和Event 是类似的，并没有多大区别。</div><div class="notion-text notion-block-1a0ed64fc993802f8646eb87b2221173">同样，Condition也只需要掌握几个函数即可。</div><div class="notion-text notion-block-1a0ed64fc9938016a4c4eb25863af96a">举个网上一个比较趣的捉迷藏的例子来看看</div><div class="notion-text notion-block-1a0ed64fc993809494e0e3b15d413e14">通过cond来通信，阻塞自己，并使对方执行。从而，达到有顺序的执行。</div><div class="notion-text notion-block-1a0ed64fc9938096b9b0d0549eb73899">看下结果</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1a0ed64fc993807498c5d05a4b4bdbdb" data-id="1a0ed64fc993807498c5d05a4b4bdbdb"><span><div id="1a0ed64fc993807498c5d05a4b4bdbdb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc993807498c5d05a4b4bdbdb" title="2.4.3 Queue队列"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2.4.3 Queue队列</b></span></span></h3><div class="notion-text notion-block-1a0ed64fc99380afb9cbe347b5d94015">最后一个，队列，它是本节的重点，因为它是我们日常开发中最使用频率最高的。</div><div class="notion-text notion-block-1a0ed64fc99380e8b745fc5328983dcc">从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了。创建一个被多个线程共享的 Queue 对象，这些线程通过使用<code class="notion-inline-code">put()</code> 和 <code class="notion-inline-code">get()</code> 操作来向队列中发送和获取元素。</div><div class="notion-text notion-block-1a0ed64fc993800e8c32d4fd56406fdd">同样，对于Queue，我们也只需要掌握几个函数即可。</div><div class="notion-text notion-block-1a0ed64fc9938079b915df1c3be37d4c">以下三个方法，知道就好，一般不需要使用</div><div class="notion-text notion-block-1a0ed64fc99380e7b3d5f133ce9bcf09">函数会比之前的多一些，同时也从另一方面说明了其功能更加丰富。</div><div class="notion-text notion-block-1a0ed64fc99380708176fde09a96642d">我来举个老师点名的例子。</div><div class="notion-text notion-block-1a0ed64fc99380d88d5ffa69dbecb54b">运行结果如下</div><div class="notion-text notion-block-1a0ed64fc993803591f0c6341277bb74">其实 queue 还有一个很重要的方法，Queue.task_done()</div><div class="notion-text notion-block-1a0ed64fc99380939b1df4bee2cd371d">如果不明白它的原理，我们在写程序，就很有可能卡死。</div><div class="notion-text notion-block-1a0ed64fc993805a9a89e581a35cd8fe">当我们使用 Queue.get() 从队列取出数据后，这个数据有没有被正常消费，是很重要的。</div><div class="notion-text notion-block-1a0ed64fc99380e79506eb7d6f5ef125">如果数据没有被正常消费，那么Queue会认为这个任务还在执行中，此时你使用 Queue.join() 会一直阻塞，即使此时你的队列里已经没有消息了。</div><div class="notion-text notion-block-1a0ed64fc993804a85b0dd8e3166f56e">那么如何解决这种一直阻塞的问题呢？</div><div class="notion-text notion-block-1a0ed64fc99380f19333c8642d92f28f">就是在我们正常消费完数据后，记得调用一下 Queue.task_done()，说明队列这个任务已经结束了。</div><div class="notion-text notion-block-1a0ed64fc9938092b673cb91a9f79dd4">当队列内部的任务计数器归于零时，调用 Queue.join() 就不会再阻塞了。</div><div class="notion-text notion-block-1a0ed64fc993807788aae1602cb57471">要理解这个过程，请参考 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://link.zhihu.com/?target=http%3A//python.iswbm.com/en/latest/c02/c02_06.html">http://python.iswbm.com/en/latest/c02/c02_06.html</a> 里自定义线程池的的例子。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a0ed64fc993818e8638f0448344b512" data-id="1a0ed64fc993818e8638f0448344b512"><span><div id="1a0ed64fc993818e8638f0448344b512" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc993818e8638f0448344b512" title="🤗 总结归纳"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🤗 总结归纳</span></span></h2><div class="notion-text notion-block-1a0ed64fc993808096cac6ff6526888f">学习了以上三种通信方法，我们很容易就能发现<code class="notion-inline-code">Event</code>和<code class="notion-inline-code">Condition</code>是threading模块原生提供的模块，原理简单，功能单一，它能发送<code class="notion-inline-code">True</code>和<code class="notion-inline-code">False</code>的指令，所以只能适用于某些简单的场景中。</div><div class="notion-text notion-block-1a0ed64fc993801083dff3a3515bbd34">而<code class="notion-inline-code">Queue</code>则是比较高级的模块，它可能发送任何类型的消息，包括字符串、字典等。其内部实现其实也引用了<code class="notion-inline-code">Condition</code>模块（譬如<code class="notion-inline-code">put</code>和<code class="notion-inline-code">get</code>函数的阻塞），正是其对<code class="notion-inline-code">Condition</code>进行了功能扩展，所以功能更加丰富，更能满足实际应用。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a0ed64fc993814f8bcde111d38653d1" data-id="1a0ed64fc993814f8bcde111d38653d1"><span><div id="1a0ed64fc993814f8bcde111d38653d1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc993814f8bcde111d38653d1" title="📎 参考文章"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📎 参考文章</span></span></h2><ul class="notion-list notion-list-disc notion-block-1a0ed64fc993817eac05d1e3d92857ff"><li>一些引用</li></ul><ul class="notion-list notion-list-disc notion-block-1a0ed64fc993813593cce22516a091ac"><li>引用文章</li></ul><div class="notion-blank notion-block-1a0ed64fc99381b7845bfbd8852836f6"> </div><div class="notion-callout notion-gray_background_co notion-block-1a0ed64fc993817f8e8ac74a26518061"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Python并发编程（三）：谈谈 Python线程中的“锁机制”]]></title>
            <link>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a0ed64f-c993-807d-a5c1-d12533aa8b66</link>
            <guid>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/1a0ed64f-c993-807d-a5c1-d12533aa8b66</guid>
            <pubDate>Thu, 20 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[当你对一段逻辑代码加锁时，意味着在同一时间有且仅能有一个线程在执行这段代码。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1a0ed64fc993807da5c1d12533aa8b66"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-1a0ed64fc993812c87c7fe2884cbe233"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">在开发中，<b>锁</b>可以理解为通行证<div class="notion-text notion-block-1a0ed64fc99380d4955be2047001fb1b">当你对一段逻辑代码加锁时，意味着在同一时间有且仅能有一个线程在执行这段代码。</div><div class="notion-text notion-block-1a0ed64fc993804cbd46ce041c3d8c28">在 Python 中的锁可以分为两种：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1a0ed64fc99380e1a732ca9b8a1372f8"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147608534&amp;content_type=Article&amp;match_order=1&amp;q=%E4%BA%92%E6%96%A5%E9%94%81&amp;zhida_source=entity">互斥锁</a></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a0ed64fc9938030bb63c63adc124ddb"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147608534&amp;content_type=Article&amp;match_order=1&amp;q=%E5%8F%AF%E9%87%8D%E5%85%A5%E9%94%81&amp;zhida_source=entity">可重入锁</a></li></ol></div></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1a0ed64fc993806883dff43542a30a8c" data-id="1a0ed64fc993806883dff43542a30a8c"><span><div id="1a0ed64fc993806883dff43542a30a8c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc993806883dff43542a30a8c" title="1.什么是锁？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>1.什么是锁？</b></span></span></h3><div class="notion-text notion-block-1a0ed64fc99380f59101db1035ef4717">在开发中，<b>锁</b>可以理解为通行证。</div><div class="notion-text notion-block-1a0ed64fc99380d1805ee76f33e2281a">当你对一段逻辑代码加锁时，意味着在同一时间有且仅能有一个线程在执行这段代码。</div><div class="notion-text notion-block-1a0ed64fc9938038a03df6d9714f05fd">在 Python 中的锁可以分为两种：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1a0ed64fc993807fa6d4f1250cd3b641"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147608534&amp;content_type=Article&amp;match_order=1&amp;q=%E4%BA%92%E6%96%A5%E9%94%81&amp;zhida_source=entity">互斥锁</a></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1a0ed64fc99380a5a87acff2a313bf0b"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147608534&amp;content_type=Article&amp;match_order=1&amp;q=%E5%8F%AF%E9%87%8D%E5%85%A5%E9%94%81&amp;zhida_source=entity">可重入锁</a></li></ol><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1a0ed64fc9938097be83cd08b0e9b9d1" data-id="1a0ed64fc9938097be83cd08b0e9b9d1"><span><div id="1a0ed64fc9938097be83cd08b0e9b9d1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc9938097be83cd08b0e9b9d1" title="2.互斥锁的使用"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2.互斥锁的使用</b></span></span></h3><div class="notion-text notion-block-1a0ed64fc99380cf897dd94a46b9ff5c">来简单看下代码，学习如何加锁，获取钥匙，释放锁。</div><div class="notion-text notion-block-1a0ed64fc99380f99e64fd1dcd99035c">需要注意的是，lock.acquire() 和 lock.release()必须成对出现。否则就有可能造成<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147608534&amp;content_type=Article&amp;match_order=1&amp;q=%E6%AD%BB%E9%94%81&amp;zhida_source=entity">死锁</a>。</div><div class="notion-text notion-block-1a0ed64fc99380f3811ff487c4e8dcba">很多时候，我们虽然知道，他们必须成对出现，但是还是难免会有忘记的时候。 为了，规避这个问题。我推荐使用使用上下文管理器来加锁。</div><div class="notion-text notion-block-1a0ed64fc99380258262ce28b88922a2"><code class="notion-inline-code">with</code> 语句会在这个代码块执行前自动获取锁，在执行结束后自动释放锁。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1a0ed64fc99380cd9375d2e88233853c" data-id="1a0ed64fc99380cd9375d2e88233853c"><span><div id="1a0ed64fc99380cd9375d2e88233853c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc99380cd9375d2e88233853c" title="3. 为何要使用锁？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>3. 为何要使用锁？</b></span></span></h3><div class="notion-text notion-block-1a0ed64fc99380fb8fb5f1c327c7cbf1">你现在肯定还是一脸懵逼，这么麻烦，我不用锁不行吗？有的时候还真不行。</div><div class="notion-text notion-block-1a0ed64fc99380f0b16fd58f542e7872">那么为了说明锁存在的意义。我们分别来看下，不用锁的情形有怎样的问题。</div><div class="notion-text notion-block-1a0ed64fc99380e8bc01d5d64a8d04a5">定义两个函数，分别在两个线程中执行。这两个函数 <code class="notion-inline-code">共用</code> 一个变量 <code class="notion-inline-code">n</code> 。</div><div class="notion-text notion-block-1a0ed64fc993806db9ecfde38380ced1">看代码貌似没什么问题，执行下看看输出</div><div class="notion-text notion-block-1a0ed64fc99380a29d0de01c2c0cf9b9">是不是很乱？完全不是我们预想的那样。</div><div class="notion-text notion-block-1a0ed64fc99380a2a878f00a6193a0ae">解释下这是为什么？因为两个线程共用一个全局变量，又由于两线程是交替执行的，当<code class="notion-inline-code">job1</code> 执行三次 <code class="notion-inline-code">+1</code> 操作时，<code class="notion-inline-code">job2</code>就不管三七二十一 给n做了<code class="notion-inline-code">+10</code>操作。两个线程之间，执行完全没有规矩，没有约束。所以会看到输出当然也很乱。</div><div class="notion-text notion-block-1a0ed64fc9938033a841d5b3f062040c">加了锁后，这个问题也就解决，来看看</div><div class="notion-text notion-block-1a0ed64fc99380eba8f1efa31621d21c">由于<code class="notion-inline-code">job1</code>的线程，率先拿到了锁，所以在for循环中，没有人有权限对n进行操作。当<code class="notion-inline-code">job1</code>执行完毕释放锁后，<code class="notion-inline-code">job2</code>这才拿到了锁，开始自己的for循环。</div><div class="notion-text notion-block-1a0ed64fc99380148d45ed1b69c4c3e6">看看执行结果，真如我们预想的那样。</div><div class="notion-text notion-block-1a0ed64fc99380feb3a5ca3cf78a15be">这里，你应该也知道了，加锁是为了对锁内资源（变量）进行锁定，避免其他线程篡改已被锁定的资源，以达到我们预期的效果。</div><div class="notion-text notion-block-1a0ed64fc9938088a6e9ebf77dafebc9">为了避免大家忘记释放锁，后面的例子，我将都使用with上下文管理器来加锁。大家注意一下。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1a0ed64fc9938069a0bdc32060fb1600" data-id="1a0ed64fc9938069a0bdc32060fb1600"><span><div id="1a0ed64fc9938069a0bdc32060fb1600" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc9938069a0bdc32060fb1600" title="4. 可重入锁（RLock）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>4. 可重入锁（RLock）</b></span></span></h3><div class="notion-text notion-block-1a0ed64fc99380b3914ee3b3bf8ad25b">有时候在同一个线程中，我们可能会多次请求同一资源，俗称锁嵌套。</div><div class="notion-text notion-block-1a0ed64fc993803d8572d552bfdf3f72">如果还是按照常规的做法，会造成死锁的。比如，下面这段代码，你可以试着运行一下。会发现并没有输出结果。</div><div class="notion-text notion-block-1a0ed64fc99380e4a0b7cfbdbd72bb56">是因为第二次获取锁(通行证)时，发现锁(通行证)已经被同一线程的人拿走了，拿东西总有个先来后到，别人拿走了，你要想用，你就得干等着，直到有人归还锁（通行证），假如别人一直不归还，那程序就会在这里一直阻塞。</div><div class="notion-text notion-block-1a0ed64fc99380619e6ee9ba591feda3">上面的代码中，使用了嵌套锁，在锁还没有释放的时候，又再一次请求锁，这就当然会造成死锁了。</div><div class="notion-text notion-block-1a0ed64fc99380edba88d5a001611a2c">那么如何解决这个问题呢？</div><div class="notion-text notion-block-1a0ed64fc99380bc8b0bf26038f1357f"><code class="notion-inline-code">threading</code>模块除了提供<code class="notion-inline-code">Lock</code>锁之外，还提供了一种可重入锁<code class="notion-inline-code">RLock</code>，专门来处理这个问题。</div><div class="notion-text notion-block-1a0ed64fc9938066b158fc31e8ff9522">执行一下，发现已经有输出了。</div><div class="notion-text notion-block-1a0ed64fc993801b9967f3803026edab">需要注意的是，可重入锁（RLock），只在同一线程里放松对锁(通行证)的获取，意思是，只要在同一线程里，程序就当你是同一个人，这个锁就可以复用，其他的话与<code class="notion-inline-code">Lock</code>并无区别。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1a0ed64fc99380dca47bdfed8cefea93" data-id="1a0ed64fc99380dca47bdfed8cefea93"><span><div id="1a0ed64fc99380dca47bdfed8cefea93" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc99380dca47bdfed8cefea93" title="5. 防止死锁的加锁机制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>5. 防止死锁的加锁机制</b></span></span></h3><div class="notion-text notion-block-1a0ed64fc99380f9b0bbd15e86ad7ad2">在编写多线程程序时，可能无意中就会写了一个死锁。可以说，死锁的形式有多种多样，但是本质都是相同的，都是对资源不合理竞争的结果。</div><div class="notion-text notion-block-1a0ed64fc99380e99a56e6efc1c4e391">以本人的经验总结，死锁通常以下几种</div><ul class="notion-list notion-list-disc notion-block-1a0ed64fc99380658353c079cdc89a94"><li>同一线程，嵌套获取同把互斥锁，造成死锁。</li></ul><ul class="notion-list notion-list-disc notion-block-1a0ed64fc99380a38ee4fb698e9d6cdf"><li>多个线程，不按顺序同时获取多个锁。造成死锁</li></ul><div class="notion-text notion-block-1a0ed64fc993807d8108c44c174c1965">对于第一种，上面已经说过了，使用可重入锁。</div><div class="notion-text notion-block-1a0ed64fc99380f3bcb3ce6b6768f4d4">主要是第二种。可能你还没明白，是如何死锁的。</div><div class="notion-text notion-block-1a0ed64fc993806eb213fda04dad1906">举个例子。</div><blockquote class="notion-quote notion-block-1a0ed64fc99380859d1af79b44ef3127"><div>线程1，嵌套获取A,B两个锁，线程2，嵌套获取B,A两个锁。 由于两个线程是交替执行的，是有机会遇到线程1获取到锁A，而未获取到锁B，在同一时刻，线程2获取到锁B，而未获取到锁A。由于锁B已经被线程2获取了，所以线程1就卡在了获取锁B处，由于是嵌套锁，线程1未获取并释放B，是不能释放锁A的，这是导致线程2也获取不到锁A，也卡住了。两个线程，各执一锁，各不让步。造成死锁。</div></blockquote><div class="notion-text notion-block-1a0ed64fc99380d7bccbf540f0ddeb1b">经过数学证明，只要两个（或多个）线程获取嵌套锁时，按照固定顺序就能保证程序不会进入死锁状态。</div><div class="notion-text notion-block-1a0ed64fc9938028999df7b08ae8b738">那么问题就转化成如何保证这些锁是按顺序的？</div><div class="notion-text notion-block-1a0ed64fc99380dc8f89cce9ab528c17">有两个办法</div><ul class="notion-list notion-list-disc notion-block-1a0ed64fc99380cc9870c11a88356338"><li>人工自觉，人工识别。</li></ul><ul class="notion-list notion-list-disc notion-block-1a0ed64fc99380e69158f5b343d407e4"><li>写一个辅助函数来对锁进行排序。</li></ul><div class="notion-text notion-block-1a0ed64fc99380d281d1d0566f1aedac">第一种，就不说了。</div><div class="notion-text notion-block-1a0ed64fc9938050849bd8026743de6a">第二种，可以参考如下代码</div><div class="notion-text notion-block-1a0ed64fc99380298b68c6032fe4a7af">如何使用呢？</div><div class="notion-text notion-block-1a0ed64fc99380ad979cf3dab2e53d22">看到没有，表面上<code class="notion-inline-code">thread_1</code>的先获取锁x，再获取锁<code class="notion-inline-code">y</code>，而<code class="notion-inline-code">thread_2</code>是先获取锁<code class="notion-inline-code">y</code>，再获取<code class="notion-inline-code">x</code>。 但是实际上，<code class="notion-inline-code">acquire</code>函数，已经对<code class="notion-inline-code">x</code>，<code class="notion-inline-code">y</code>两个锁进行了排序。所以<code class="notion-inline-code">thread_1</code>，<code class="notion-inline-code">hread_2</code>都是以同一顺序来获取锁的，是不是造成死锁的。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1a0ed64fc99380a891fdebefc298edb3" data-id="1a0ed64fc99380a891fdebefc298edb3"><span><div id="1a0ed64fc99380a891fdebefc298edb3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc99380a891fdebefc298edb3" title="6. 饱受争议的GIL（全局锁）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>6. 饱受争议的</b><b><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147608534&amp;content_type=Article&amp;match_order=1&amp;q=GIL&amp;zhida_source=entity">GIL</a></b><b>（全局锁）</b></span></span></h3><div class="notion-text notion-block-1a0ed64fc993806795a8d2ef31cf63d8">在第一章的时候，我就和大家介绍到，多线程和多进程是不一样的。</div><div class="notion-text notion-block-1a0ed64fc99380959b85de926208a24d">多进程是真正的并行，而多线程是伪并行，实际上他只是交替执行。</div><div class="notion-text notion-block-1a0ed64fc9938035a847d90d3c4dec33">是什么导致多线程，只能交替执行呢？是一个叫<code class="notion-inline-code">GIL</code>（<code class="notion-inline-code">Global Interpreter Lock</code>，全局解释器锁）的东西。</div><div class="notion-text notion-block-1a0ed64fc99380c5aa8eec719360446e">什么是GIL呢？</div><blockquote class="notion-quote notion-block-1a0ed64fc99380328436eff523e81f32"><div>任何Python线程执行前，必须先获得GIL锁，然后，每执行100条字节码，解释器就自动释放GIL锁，让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁，所以，多线程在Python中只能交替执行，即使100个线程跑在100核CPU上，也只能用到1个核。</div></blockquote><div class="notion-text notion-block-1a0ed64fc9938019b081f09af322c251">需要注意的是，GIL并不是Python的特性，它是在实现Python解析器(<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://zhida.zhihu.com/search?content_id=147608534&amp;content_type=Article&amp;match_order=1&amp;q=CPython&amp;zhida_source=entity">CPython</a>)时所引入的一个概念。而Python解释器，并不是只有CPython，除它之外，还有<code class="notion-inline-code">PyPy</code>，<code class="notion-inline-code">Psyco</code>，<code class="notion-inline-code">JPython</code>，<code class="notion-inline-code">IronPython</code>等。</div><div class="notion-text notion-block-1a0ed64fc993809eb385d7242996815b">在绝大多数情况下，我们通常都认为 Python <code class="notion-inline-code">==</code> CPython，所以也就默许了Python具有GIL锁这个事。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a0ed64fc993811cb025e3fe3eeed4b1" data-id="1a0ed64fc993811cb025e3fe3eeed4b1"><span><div id="1a0ed64fc993811cb025e3fe3eeed4b1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc993811cb025e3fe3eeed4b1" title="🤗 总结归纳"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🤗 总结归纳</span></span></h2><div class="notion-text notion-block-1a0ed64fc993817dac1fcfb9998219e4">都知道GIL影响性能，那么如何避免受到GIL的影响？</div><ul class="notion-list notion-list-disc notion-block-1a0ed64fc9938037b0bbf887e563c103"><li>使用多进程代替多线程。</li></ul><ul class="notion-list notion-list-disc notion-block-1a0ed64fc9938059ba4de83cb966dc73"><li>更换Python解释器，不使用CPython</li></ul><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1a0ed64fc99381aab45bf3dcc6a3fc0d" data-id="1a0ed64fc99381aab45bf3dcc6a3fc0d"><span><div id="1a0ed64fc99381aab45bf3dcc6a3fc0d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1a0ed64fc99381aab45bf3dcc6a3fc0d" title="📎 参考文章"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📎 参考文章</span></span></h2><ul class="notion-list notion-list-disc notion-block-1a0ed64fc99381a4b789c02a29b312a9"><li>一些引用</li></ul><ul class="notion-list notion-list-disc notion-block-1a0ed64fc99381008784e23e380abd8d"><li>引用文章</li></ul><div class="notion-blank notion-block-1a0ed64fc9938133badfee2cfc6322d6"> </div><div class="notion-callout notion-gray_background_co notion-block-1a0ed64fc99381dfbd63cbf60c638db7"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Python并发编程（二）：创建多线程的几种方法]]></title>
            <link>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/19fed64f-c993-805f-ae8f-caaeff0eabe5</link>
            <guid>桂林第一深情 | 冰冰冰淇淋的个人博客 (bbbql.top)/article/19fed64f-c993-805f-ae8f-caaeff0eabe5</guid>
            <pubDate>Wed, 19 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[在Python3中，Python提供了一个内置模块 threading.Thread，可以很方便地让我们创建多线程。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-19fed64fc993805fae8fcaaeff0eabe5"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-19fed64fc993817eb7a4d11fd7d25120"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">这里写文章的前言：
一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么？并简述你给出的答案。<div class="notion-text notion-block-19fed64fc993817499e0d0ad67bad793">可以说说你的故事：阻碍、努力、结果成果，意外与转折。</div></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-19fed64fc993817f8300e9e25756824f" data-id="19fed64fc993817f8300e9e25756824f"><span><div id="19fed64fc993817f8300e9e25756824f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19fed64fc993817f8300e9e25756824f" title="📝 主旨内容"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📝 主旨内容</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-19fed64fc993802d84e1d4202b204dc4" data-id="19fed64fc993802d84e1d4202b204dc4"><span><div id="19fed64fc993802d84e1d4202b204dc4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19fed64fc993802d84e1d4202b204dc4" title="1. 用函数创建多线程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>1. 用函数创建多线程</b></span></span></h3><div class="notion-text notion-block-19fed64fc99380b2ad00fbfca986d679">在Python3中，Python提供了一个内置模块 <code class="notion-inline-code">threading.Thread</code>，可以很方便地让我们创建多线程。</div><div class="notion-text notion-block-19fed64fc99380c4ad91fb1a783ee062"><code class="notion-inline-code">threading.Thread()</code> 一般接收两个参数：</div><ul class="notion-list notion-list-disc notion-block-19fed64fc99380dfb4fcfb21e823954d"><li>线程函数名：要放置线程让其后台执行的函数，由我们自已定义，注意不要加<code class="notion-inline-code">()</code>；</li></ul><ul class="notion-list notion-list-disc notion-block-19fed64fc993806cb93af75dc1627a73"><li>线程函数的参数：线程函数名所需的参数，以元组的形式传入。若不需要参数，可以不指定。</li></ul><div class="notion-text notion-block-19fed64fc993808581ecd7876b59447c"><b>举个例子</b></div><div class="notion-text notion-block-19fed64fc99380778be6f87bfcd2faaa">可以看到输出</div><div class="notion-text notion-block-19fed64fc9938056a58ec0dbda6864b5"><b>2. 用类创建多线程</b></div><div class="notion-text notion-block-19fed64fc99380a2a128c0e9a7209be9">相比较函数而言，使用类创建线程，会比较麻烦一点。</div><div class="notion-text notion-block-19fed64fc9938052aca2fbdb24279773">首先，我们要自定义一个类，对于这个类有两点要求，</div><ul class="notion-list notion-list-disc notion-block-19fed64fc99380e2ae16f5cb7798d7ee"><li>必须继承 <code class="notion-inline-code">threading.Thread</code> 这个父类；</li></ul><ul class="notion-list notion-list-disc notion-block-19fed64fc99380ecaf82c6bed09a5ab7"><li>必须复写 <code class="notion-inline-code">run</code> 方法。</li></ul><div class="notion-text notion-block-19fed64fc993806cb88df8f99e2c16c0">这里的 <code class="notion-inline-code">run</code> 方法，和我们上面<code class="notion-inline-code">线程函数</code>的性质是一样的，可以写我们的业务逻辑程序。在 <code class="notion-inline-code">start()</code> 后将会调用。</div><div class="notion-text notion-block-19fed64fc9938009b156ec649633bc34">来看一下例子 为了方便对比，<code class="notion-inline-code">run</code>函数我复用上面的<code class="notion-inline-code">main</code>。</div><div class="notion-text notion-block-19fed64fc993803faca6c5ec406e8e88">当然结果也是一样的。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-19fed64fc99380e6b940c07daaf1b728" data-id="19fed64fc99380e6b940c07daaf1b728"><span><div id="19fed64fc99380e6b940c07daaf1b728" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19fed64fc99380e6b940c07daaf1b728" title="3. 线程对象的方法"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>3. 线程对象的方法</b></span></span></h3><div class="notion-text notion-block-19fed64fc99380549499d4c5d963d993">上面介绍了当前 Python 中创建线程两种主要方法。</div><div class="notion-text notion-block-19fed64fc99380f4b68eed438809eda0">创建线程是件很容易的事，但要想用好线程，还需要学习线程对象的几个函数。</div><div class="notion-text notion-block-19fed64fc993807fbbe3ec0fbbf7a352">经过我的总结，大约常用的方法有如下这些：</div><div class="notion-text notion-block-19fed64fc9938073a810dd2d15f5af31">至此，Python线程基础知识，我们大概都介绍完了。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-19fed64fc9938180a513d66ed868a7d6" data-id="19fed64fc9938180a513d66ed868a7d6"><span><div id="19fed64fc9938180a513d66ed868a7d6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19fed64fc9938180a513d66ed868a7d6" title="🤗 总结归纳"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🤗 总结归纳</span></span></h2><div class="notion-text notion-block-19fed64fc99381618211fbe021a05ed1">总结文章的内容</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-19fed64fc99381c2b4dbf94d66e2e189" data-id="19fed64fc99381c2b4dbf94d66e2e189"><span><div id="19fed64fc99381c2b4dbf94d66e2e189" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19fed64fc99381c2b4dbf94d66e2e189" title="📎 参考文章"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📎 参考文章</span></span></h2><ul class="notion-list notion-list-disc notion-block-19fed64fc99381d49744e64567148cfd"><li>一些引用</li></ul><ul class="notion-list notion-list-disc notion-block-19fed64fc99381a0ad14f9b326f95661"><li>引用文章</li></ul><div class="notion-blank notion-block-19fed64fc993813389f0dbff112e871b"> </div><div class="notion-callout notion-gray_background_co notion-block-19fed64fc9938110836effc617fde6cd"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>