<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>JZX 轻语</title>
    <description>挖掘时光的细节</description>
    <link>https://www.jeza.net/</link>
    <atom:link href="https://www.jeza.net/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Sat, 07 Feb 2026 15:03:26 +0000</pubDate>
    <lastBuildDate>Sat, 07 Feb 2026 15:03:26 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Git连接提示&quot;Connection closed by xxx&quot;的解决方法</title>
        <description>&lt;p&gt;今天给自己的Ubuntu系统配置git的时候，发现没法连接Github，clone/fetch/pull此类涉及网络连接的操作都会出错，并提示”Connection closed by 20.205.243.166 port 22”。一开始以为是SSH密钥或者路由器DNS有问题，但重置了一遍后还是报错。经过查阅资料发现，可能是因为网络中的某一环（防火墙/代理服务器）屏蔽掉了22端口导致的。我们可以键入以下命令尝试是否可以通过443端口SSH连接到Github上：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;ssh &lt;span class=&quot;nt&quot;&gt;-T&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 443 git@ssh.github.com&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;如果提示如下信息，说明可以通过443端口连接到Github：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;Hi {Your Github Username}! You&apos;ve successfully authenticated, but GitHub does not provide shell access.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;此时我们可以曲线救国，配置git使用443端口进行SSH连接。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;具体方法是编辑&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ssh/config&lt;/code&gt;文件（如果没有则新建一个），添加如下内容：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;[url &quot;ssh://git@ssh.github.com:443/&quot;]
insteadOf = git@github.com:&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;上述配置新增一个&lt;strong&gt;路径重写（URL Rewriting）&lt;/strong&gt;规则：每当执行Git操作（如clone、push、pull）且目标地址以“&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git@github.com:&lt;/code&gt;”开头时（通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;insteadOf&lt;/code&gt;字段指定），Git会自动将其替换为“&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh://git@ssh.github.com:443/&lt;/code&gt;”。比如，当你执行命令：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;git clone git@github.com:owner/repo.git&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Git会转换为：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;git clone ssh://git@ssh.github.com:443/owner/repo.git&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;这样就能通过443端口与Github建立SSH连接，绕过22端口被屏蔽的问题。&lt;/p&gt;

&lt;h2 id=&quot;参考资料&quot;&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/orgs/community/discussions/54558&quot;&gt;Unable to use github ssh for anything (pull/clone/push etc) #54558&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://docs.github.com/en/authentication/troubleshooting-ssh/using-ssh-over-the-https-port&quot;&gt;Using SSH over the HTTPS port&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 09 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2026/01/09/Git-Connection-Closed/</link>
        <guid isPermaLink="true">https://www.jeza.net/2026/01/09/Git-Connection-Closed/</guid>
        
        <category>Git</category>
        
        <category>杂七杂八</category>
        
        
      </item>
    
      <item>
        <title>跨仓库应用Git提交的方法</title>
        <description>&lt;p&gt;最近在处理公司项目时，遇到需要将一个Git仓库中的某些提交应用到另一个仓库中的情况。由于是两个分离的仓库，直接使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git cherry-pick&lt;/code&gt;命令并不可行。经过一番研究，发现可以通过导出补丁文件并应用到目标仓库来实现这一需求。本文分享具体的操作步骤和注意事项。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;太长不看&quot;&gt;太长不看&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;在源仓库中，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git format-patch&lt;/code&gt;命令将所需要的提交导出成补丁文件。如果需要将多个提交合并成一个补丁文件，可以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git format-patch -&amp;lt;n&amp;gt; HEAD --stdout &amp;gt; &amp;lt;patch-file&amp;gt;&lt;/code&gt;命令。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在目标仓库中，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;或者&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git am&lt;/code&gt;命令将补丁文件应用到目标仓库。如果两个仓库的目录结构不一样，可使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&amp;lt;n&amp;gt;&lt;/code&gt;选项和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--directory&lt;/code&gt;选项来调整路径。比如，将仓库A中关于文件&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/path/main.cpp&lt;/code&gt;的修改应用到仓库B中对应的文件&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Source/path/main.cpp&lt;/code&gt;，可以使用如下命令：&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git apply &amp;lt;patch-file&amp;gt; &lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-p2&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# remove 2 leading directory (&quot;a/src/&quot;) from the path in the patch&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Source&quot;&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# prepend &quot;Source/&quot; to the path&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;适用场景&quot;&gt;适用场景&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;两个仓库A, B中具有相同名字的文件，且文件的内容大致相同，希望能将仓库A中对此文件的修改”cherry-pick”到仓库B中（当然，cherry-pick只能用于同一仓库的不同分支中，并不支持跨仓库提交）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;两个仓库具有几乎一致的源码，希望能将仓库A的某个commit应用在仓库B中。这种情况一般发生在工程结构的重构，比如将一个仓库内的某个目录抽出来，单独作为一个新的仓库来维护。但前面的仓库因为某种历史原因，该目录仍未删除，形成了同一份代码（或工程），两个仓库并行维护的状态。因此在开发的过程中，需要往两个仓库提交几乎一致的代码。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;具体过程&quot;&gt;具体过程&lt;/h2&gt;

&lt;h3 id=&quot;示例仓库结构&quot;&gt;示例仓库结构&lt;/h3&gt;

&lt;p&gt;以&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepoSample&quot;&gt;一个示例仓库&lt;/a&gt;为例，我们先用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone --recursive https://github.com/JezaChen/ApplyPatchAcrossRepoSample&lt;/code&gt;将其clone到本地。里面有两个子仓库”repoA”以及”repoB”，其中repoA的目录结构如下：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;repoA/
  ├── src/
      └── path
            └── main.cpp&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;repoB的目录结构如下：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;repoB/
  ├── src/
      └── Source
            └── main.cpp&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;而&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoA/blob/main/src/path/main.cpp&quot;&gt;repoA里面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;&lt;/a&gt;和&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoB/blob/main/Source/path/main.cpp&quot;&gt;repoB里面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;&lt;/a&gt;内容大致相同。除了第8行所传入的参数不一样，其他内容是相同的。&lt;/p&gt;

&lt;h3 id=&quot;输出补丁&quot;&gt;输出补丁&lt;/h3&gt;

&lt;p&gt;我们对repoA的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;进行改动，切去&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoA/tree/dev&quot;&gt;新的分支&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev&lt;/code&gt;&lt;/a&gt;，在&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoA/commit/da93915&quot;&gt;第一个提交（da93915）&lt;/a&gt;中，将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add&lt;/code&gt;函数修改成&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The sum is: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;然后，在&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoA/commit/7669cbd&quot;&gt;第二个提交（7669cbd）&lt;/a&gt;中，给&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add&lt;/code&gt;函数新增一行注释&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// This function adds two integers and returns the result&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The sum is: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;如果我们想将这两个提交应用到repoB中，要怎么做呢？最直观的想法是使用cherry-pick，但cherry-pick只能用于同一仓库的不同分支中，并不支持跨仓库提交。这时候，git的补丁机制就派上用场了，我们可以将这两个提交导出成补丁文件，然后再将补丁文件应用到repoB中。&lt;/p&gt;

&lt;p&gt;使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git format-patch&lt;/code&gt;命令导出补丁文件。在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev&lt;/code&gt;分支上，使用以下命令导出最近两次的提交：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git format-patch &lt;span class=&quot;nt&quot;&gt;-2&lt;/span&gt; HEAD&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;不幸的是，执行之后，会发现这个命令生成了两个补丁文件——能否将其合并成一个呢？当然可以！我们可以通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--stdout&lt;/code&gt;选项将其打印到终端上，然后重定向一个文件里（在例中，我们命名为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my.patch&lt;/code&gt;）：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git format-patch &lt;span class=&quot;nt&quot;&gt;-2&lt;/span&gt; HEAD &lt;span class=&quot;nt&quot;&gt;--stdout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; my.patch&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;最后，我们将所输出的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my.patch&lt;/code&gt;文件移动到repoB里。&lt;/p&gt;

&lt;h3 id=&quot;应用补丁&quot;&gt;应用补丁&lt;/h3&gt;

&lt;p&gt;补丁有了，怎么应用到目标仓库（repoB）里面呢？我们不难想到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;命令，但直接应用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply my.patch&lt;/code&gt;命令会报错，毕竟两个仓库的目录结构是有区别的。&lt;/p&gt;

&lt;p&gt;还好，Git提供了灵活的选项——我们可以利用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&amp;lt;n&amp;gt;&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--directory&lt;/code&gt;来fix上述的问题：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git apply my.patch &lt;span class=&quot;nt&quot;&gt;-p2&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Source&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;其中，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&amp;lt;n&amp;gt;&lt;/code&gt;选项用于去掉补丁文件中路径的前&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt;个目录层级，在本例中，补丁文件中的路径是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a/src/path/main.cpp&lt;/code&gt;（注意补丁文件里面有个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a/&lt;/code&gt;），我们需要去掉前两个目录层级&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a/&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/&lt;/code&gt;，使其变成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;path/main.cpp&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--directory=&quot;Source&quot;&lt;/code&gt;选项则是告诉&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;命令，将补丁应用到当前目录下的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Source&lt;/code&gt;目录中。最终，我们应用补丁的路径就是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Source/path/main.cpp&lt;/code&gt;，正好对应上了repoB中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;命令执行完毕后，我们可以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt;命令查看repoB的状态，发现&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;文件已经被修改了，且改动的内容是一致的！&lt;/p&gt;

&lt;p&gt;除了上述的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;命令外，我们也可以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git am&lt;/code&gt;命令来应用补丁文件，命令如下，可以发现与&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;命令类似：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git am my.patch &lt;span class=&quot;nt&quot;&gt;-p2&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Source&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;两者的区别在于，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git am&lt;/code&gt;应用补丁后会自动帮你提交，甚至能保留源仓库中的提交信息（比如作者、提交时间、提交说明等）；而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;则只会应用补丁的内容，不会保留原有的提交信息。&lt;/strong&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 05 Nov 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/11/05/Git-Apply-Patch-Across-Repos/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/11/05/Git-Apply-Patch-Across-Repos/</guid>
        
        <category>Git</category>
        
        <category>杂七杂八</category>
        
        
      </item>
    
      <item>
        <title>contextlib.contextmanager源码分析</title>
        <description>&lt;p&gt;之前一直想写&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;的实现原理，但也一直都没时间，今天看了&lt;a href=&quot;https://realpython.com/python-with-statement/&quot;&gt;Real Python关于上下文管理器的文章&lt;/a&gt;，打算补一下之前的坑。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;预备知识&quot;&gt;预备知识&lt;/h2&gt;

&lt;p&gt;Python的语法糖很多，而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;主要涉及了三个比较常用的语法糖：&lt;em&gt;装饰器&lt;/em&gt;、&lt;em&gt;生成器&lt;/em&gt;和&lt;em&gt;上下文管理器&lt;/em&gt;（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句）。&lt;/p&gt;

&lt;h3 id=&quot;装饰器&quot;&gt;装饰器&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;装饰器&lt;/em&gt;本质上原理很简单：它无非就是起到一种“锦上添花”的功能，在原有的函数上补充功能。被包装的函数以参数的形式传入到&lt;em&gt;装饰器&lt;/em&gt;。也就是&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@decorator&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;本质上等价于&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;decorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;生成器&quot;&gt;生成器&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;生成器&lt;/em&gt;则可以看成能&lt;strong&gt;中途停下来（暂停的时候还能通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;往外部抛出值），等待下一步指令并继续执行&lt;/strong&gt;（外部可调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt;方法传进指令，也可以直接通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next(...)&lt;/code&gt;让它继续）的函数。这里不再赘述，直接来个简单的例子：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gen(): Start...&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;first_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gen(): &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;second_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gen(): &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;second_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__main__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;recv1: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;recv2: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Command1&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;recv3: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Command2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This will raise StopIteration
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可以看到，我们首先调用&lt;em&gt;生成器函数&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gen&lt;/code&gt;得到一个&lt;em&gt;生成器迭代器&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g&lt;/code&gt;，然后使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next(g)&lt;/code&gt;激活&lt;em&gt;生成器迭代器&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g&lt;/code&gt;（此时会输出&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;gen(): Start...&quot;&lt;/code&gt;），激活后，会返回一个字符串&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;First&quot;&lt;/code&gt;。然后，我们使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g.send(&apos;Command1&apos;)&lt;/code&gt;发送字符串&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;Command1&apos;&lt;/code&gt;到&lt;em&gt;生成器迭代器&lt;/em&gt;中，此时&lt;em&gt;生成器迭代器&lt;/em&gt;内的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_command = yield &quot;First&quot;&lt;/code&gt;收到外部传入的参数并赋值&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_command&lt;/code&gt;，也就是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_command=&apos;Command1&apos;&lt;/code&gt;，并继续执行下去。当&lt;em&gt;生成器迭代器&lt;/em&gt;到达终点时，其会抛出一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StopIteration&lt;/code&gt;异常告知外部已经。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;需要注意的是，如果外部不使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt;发送参数而是直接调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next&lt;/code&gt;，则&lt;em&gt;生成器迭代器&lt;/em&gt;内通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;表达式所收到的值为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里厘清一下生成器相关的术语：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/zh-cn/3.13/glossary.html#term-generator&quot;&gt;&lt;em&gt;生成器函数&lt;/em&gt;&lt;/a&gt;（generator function，大多数场合下，&lt;em&gt;生成器&lt;/em&gt;（generator）指的是&lt;em&gt;生成器函数&lt;/em&gt;）：函数体内包含&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;语句的函数，调用它会返回一个&lt;em&gt;生成器迭代器&lt;/em&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/zh-cn/3.13/glossary.html#term-generator&quot;&gt;&lt;em&gt;生成器迭代器&lt;/em&gt;&lt;/a&gt;（generator iterator）：&lt;em&gt;生成器函数&lt;/em&gt;所创建的对象，其实现了迭代器协议（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__iter__&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__next__&lt;/code&gt;方法），具有&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;throw&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;close&lt;/code&gt;等方法。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一般来说，&lt;em&gt;生成器&lt;/em&gt;指的是&lt;em&gt;生成器函数&lt;/em&gt;，但有时候也会指&lt;em&gt;生成器迭代器&lt;/em&gt;，具体要看上下文。&lt;/p&gt;

&lt;h3 id=&quot;上下文管理器&quot;&gt;上下文管理器&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;上下文管理器&lt;/em&gt;则是实现了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;这两个魔术方法的类，使用时，将该类的&lt;strong&gt;实例&lt;/strong&gt;传入到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句即可。&lt;/p&gt;

&lt;p&gt;我们可以简单实现一个&lt;em&gt;上下文管理器&lt;/em&gt;，其会在进出上下文的时候输出一些信息，并且捕获&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句体内的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZeroDivisionError&lt;/code&gt;异常。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Entering the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Exiting the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Handled ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Suppress the exception
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Propagate other exceptions&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;我们可以写个简单的使用例子：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__main__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;OK, no exception&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This will raise ZeroDivisionError
&lt;/span&gt;    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Go ahead, no exception propagated&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;This line will not be printed due to IndexError&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;运行程序，可以看到无论是否有异常，&lt;em&gt;上下文管理器&lt;/em&gt;都能执行到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;方法，从而确保其所托管的资源能释放。当然，如果异常继续传播，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句之后的代码也不会执行了（除非遇到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finally&lt;/code&gt;语句）。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;Entering the context
s = &apos;Hello&apos;
Exiting the context
exc_type  = None
exc_value = None
traceback = None
OK, no exception

Entering the context
Exiting the context
exc_type  = &amp;lt;class &apos;ZeroDivisionError&apos;&amp;gt;
exc_value = ZeroDivisionError(&apos;division by zero&apos;)
traceback = &amp;lt;traceback object at 0x000001AA8C7CFA40&amp;gt;
Handled ZeroDivisionError
Go ahead, no exception propagated

Entering the context
Exiting the context
exc_type  = &amp;lt;class &apos;IndexError&apos;&amp;gt;
exc_value = IndexError(&apos;list assignment index out of range&apos;)
traceback = &amp;lt;traceback object at 0x000001AA8C7CF9C0&amp;gt;
python-BaseException
Traceback (most recent call last):
  File &quot;C:\Users\Jeza\PyQtEx\test1.py&quot;, line 43, in &amp;lt;module&amp;gt;
    a[1] = -1
    ~^^^
IndexError: list assignment index out of range&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;contextmanager的简单用法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;的简单用法&lt;/h2&gt;

&lt;p&gt;主角终于登场，现在我们使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;对上文的&lt;em&gt;上下文管理器类&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SimpleCtxManager&lt;/code&gt;做个等价的改造：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@contextlib.contextmanager&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Entering the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;exc_type = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;exc_value = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;traceback = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__traceback__&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Handled ZeroDivisionError:&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Exiting the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可以看到，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;是一个&lt;em&gt;装饰器&lt;/em&gt;，其所包装的是一个&lt;em&gt;生成器函数&lt;/em&gt;，其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;语句前面的代码可以看成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;的逻辑，而后面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;except…finally…&lt;/code&gt;语句则可以看成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;的逻辑（处理异常/清理资源）。对比发现，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt; 不仅能让代码易读（看起来更流程化），而且不用专门写一个类，仅需一个&lt;em&gt;生成器函数&lt;/em&gt;即可做到一样的功能。&lt;/p&gt;

&lt;h2 id=&quot;contextmanager的源码分析&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;的源码分析&lt;/h2&gt;

&lt;p&gt;OK，终于讲到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;的原理了。我们可以在Python官方标准库中找到&lt;a href=&quot;https://github.com/python/cpython/blob/main/Lib/contextlib.py&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.py&lt;/code&gt;的源代码&lt;/a&gt;，定位到&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L272&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;的实现&lt;/a&gt;：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;contextmanager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@wraps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;helper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_GeneratorContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;helper&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;由代码看出，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;是一个&lt;em&gt;装饰器&lt;/em&gt;，其接受一个函数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func&lt;/code&gt;作为参数，在闭包&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helper&lt;/code&gt;内用它来继续构造&lt;em&gt;上下文管理器&lt;/em&gt;类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManager&lt;/code&gt;的实例并返回。我们将&lt;em&gt;装饰器&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;所返回的闭包&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helper&lt;/code&gt;视作为一个&lt;em&gt;上下文管理器&lt;/em&gt;工厂（在外部看起来，它其实用法和&lt;em&gt;上下文管理器&lt;/em&gt;是一样的），其接受任意的参数，负责在使用处构造出真正的&lt;em&gt;上下文管理器&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManager&lt;/code&gt;的实例。&lt;/p&gt;

&lt;h3 id=&quot;三个基类的分析&quot;&gt;三个基类的分析&lt;/h3&gt;

&lt;p&gt;我们继续研究&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L125&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManager&lt;/code&gt;类的实现&lt;/a&gt;，首先看看它的基类：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;_GeneratorContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_GeneratorContextManagerBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ContextDecorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可以看到其继承了三个基类，我们首先看第一个基类&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L101&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManagerBase&lt;/code&gt;的实现&lt;/a&gt;，这也是所有基于&lt;em&gt;生成器&lt;/em&gt;的&lt;em&gt;上下文管理器&lt;/em&gt;的公共基类：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;_GeneratorContextManagerBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Shared functionality for @contextmanager and @asynccontextmanager.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Issue 19330: ensure context manager instances have good docstrings
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__doc__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__doc__&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__doc__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Unfortunately, this still doesn&apos;t provide good help output when
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# inspecting the created context manager instances, since pydoc
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# currently bypasses the instance docstring and shows the docstring
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# for the class instead.
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# See http://bugs.python.org/issue19404 for more details.
&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_recreate_cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# _GCMB instances are one-shot context managers, so the
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# CM must be recreated each time a decorated function is
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# called
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;__class__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;不难分析，这个基类所实现的逻辑比较简单：保存&lt;em&gt;生成器函数&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func&lt;/code&gt;以及调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func&lt;/code&gt;所需的参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;args&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kwds&lt;/code&gt; 、将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func&lt;/code&gt;的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__doc__&lt;/code&gt;属性复制到&lt;em&gt;生成器&lt;/em&gt;里面去，以及提供一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_recreate_cm&lt;/code&gt;方法，以用来&lt;strong&gt;重新创建一个新的&lt;em&gt;上下文管理器&lt;/em&gt;实例&lt;/strong&gt;，保证&lt;strong&gt;该实例作为&lt;em&gt;装饰器&lt;/em&gt;使用&lt;/strong&gt;时，在每一次函数调用都能秽土重生，&lt;strong&gt;重新生成一个全新的实例&lt;/strong&gt;用来执行上下文——因为&lt;em&gt;生成器迭代器&lt;/em&gt;是一次性的，一旦激活并&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next&lt;/code&gt;下去，就没有回头路了。需要回头？只能重来。（让&lt;em&gt;生成器函数&lt;/em&gt;再生一个出来）&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;再次强调下，这里的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_recreate_cm&lt;/code&gt;方法仅用于该上下文管理器实例作为装饰器使用的场合。其覆写了第三个基类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContextDecorator&lt;/code&gt;的同名方法，后面会讲到。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;这个上下文管理器的实例如何作为装饰器使用呢？后面第三个基类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContextDecorator&lt;/code&gt;的分析会有例子。这两个基类的关系很密切。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;OK，我们来看第二个基类&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L17&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbstractContextManager&lt;/code&gt;的实现&lt;/a&gt;：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ABC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;An abstract base class for context managers.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;__class_getitem__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;classmethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenericAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Return `self` upon entering the runtime context.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@abc.abstractmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Raise any exception triggered within the runtime context.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__subclasshook__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_collections_abc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_check_methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NotImplemented&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;这个没什么好说的，其就是所有&lt;em&gt;上下文管理器&lt;/em&gt;的&lt;em&gt;基类&lt;/em&gt;。当然，由于Python是鸭子类型语言，并不强制&lt;em&gt;上下文管理器&lt;/em&gt;的实现必须继承这个基类，这个基类只是用来&lt;strong&gt;加强类型检查&lt;/strong&gt;，避免&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;或&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;方法没有实现（即上述的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__subclasshook__&lt;/code&gt;所做的）。比如在下面的例子中，我们定义&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WrongCtxManager&lt;/code&gt;类的时候，犯了个很致命的错误——只实现了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;却漏了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;——还好！由于这个类继承了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbstractContextManager&lt;/code&gt;，在实例化的时候Python解释器会“贴心”地抛出异常，告知你没有实现完整的上下文管理协议。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contextlib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WrongCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Entering the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WrongCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# error!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;del&gt;可见当鹅比当鸭好！&lt;/del&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我们来看第三个基类&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L62&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContextDecorator&lt;/code&gt;&lt;/a&gt;：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ContextDecorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;A base class or mixin that enables context managers to work as decorators.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_recreate_cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Return a recreated instance of self.

        Allows an otherwise one-shot context manager like
        _GeneratorContextManager to support use as
        a decorator via implicit recreation.

        This is a private interface just for _GeneratorContextManager.
        See issue #11647 for details.
        &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__call__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@wraps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;inner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_recreate_cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inner&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;这个基类更像是一个mixin，为子类实现了一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__call__&lt;/code&gt;魔术方法，使得&lt;strong&gt;上下文管理器实例可作为&lt;em&gt;装饰器&lt;/em&gt;使用&lt;/strong&gt;。在被包装函数调用的前后，执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;方法。关于&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_recreate_cm&lt;/code&gt;，前面已经提前路透了，这里不再多说了。&lt;/p&gt;

&lt;p&gt;以前面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SimpleCtxManager&lt;/code&gt;为例，我们为它加上父类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContextDecorator&lt;/code&gt;，此时&lt;strong&gt;其实例&lt;/strong&gt;就可以作为&lt;em&gt;装饰器&lt;/em&gt;来用了。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contextlib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContextDecorator&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContextDecorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Inherit from ContextDecorator
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Entering the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Exiting the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Handled ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Suppress the exception
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Propagate other exceptions
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@cm&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Use the instance as a decorator
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;func...&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; 

&lt;span class=&quot;c1&quot;&gt;# Outputs:
# Entering the context
# func...
# Exiting the context
# exc_type  = None
# exc_value = None
# traceback = None&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;回到contextlibcontextmanager&quot;&gt;回到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;好了，分析完三个基类后，我们终于轮到分析主角&lt;a href=&quot;https://github.com/python/cpython/blob/main/Lib/contextlib.py#L129&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;&lt;/a&gt;了，可以看到，通过继承上述的三个基类后，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;只需将精力花在最核心的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;两个方法：&lt;/p&gt;

&lt;p&gt;&lt;em&gt;（为减少篇幅，下面的源码去掉了注释）&lt;/em&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;_GeneratorContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_GeneratorContextManagerBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ContextDecorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;del&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RuntimeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;generator didn&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;t yield&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RuntimeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;generator didn&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;t stop&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;typ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RuntimeError&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__traceback__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__cause__&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__traceback__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;BaseException&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__traceback__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RuntimeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;generator didn&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;t stop after throw()&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可以看到，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManager&lt;/code&gt;是幕后真正起作用的&lt;em&gt;上下文管理器&lt;/em&gt;。我们抛开一些细节性的校验逻辑，分析下&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;的主要思路：使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next(self.gen)&lt;/code&gt;激活&lt;em&gt;生成器迭代器&lt;/em&gt;（也就是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;包裹的&lt;em&gt;生成器函数&lt;/em&gt;所返回的&lt;em&gt;生成器迭代器&lt;/em&gt;），使其走到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;语句处停下——这也意味着，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;语句&lt;strong&gt;前面所执行的代码&lt;/strong&gt;拿来充当&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;的逻辑了！&lt;/p&gt;

&lt;p&gt;我们再看&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;的实现思路，分两种情况：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句块&lt;strong&gt;没有抛出异常&lt;/strong&gt;：使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next(self.gen)&lt;/code&gt;恢复&lt;em&gt;生成器迭代器&lt;/em&gt;，并&lt;strong&gt;期望它能执行到最后（包括走完finally语句块）&lt;/strong&gt;。如果发现&lt;em&gt;生成器迭代器&lt;/em&gt;暂停了（说明又遇到了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;），则这个&lt;em&gt;生成器函数&lt;/em&gt;的实现不符合&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;的规范，抛出&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RuntimeError&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;如果&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句块&lt;strong&gt;抛出了异常&lt;/strong&gt;：将异常转发到&lt;em&gt;生成器迭代器&lt;/em&gt;的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt; 表达式处（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self.gen.throw(typ, value, traceback)&lt;/code&gt;），给它一个机会做清理或选择抑制异常，然后再根据它所抛出的异常再做处理——
    &lt;ul&gt;
      &lt;li&gt;如果抛出的是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StopIteration&lt;/code&gt;&lt;strong&gt;且其异常实例不同于所传入的异常实例&lt;/strong&gt;（确保了这个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StopIteration&lt;/code&gt;是&lt;em&gt;生成器迭代器&lt;/em&gt;&lt;strong&gt;自己执行完毕&lt;/strong&gt;所抛出的，而非重新&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raise&lt;/code&gt;了所传入的异常（尤其这个所传的异常也是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StopIteration&lt;/code&gt;）），意味着它选择了抑制异常，此时&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;返回&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt;；&lt;/li&gt;
      &lt;li&gt;如果抛出的是其他异常，则说明&lt;em&gt;生成器&lt;/em&gt;选择了不抑制异常，此时&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;返回&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;False&lt;/code&gt;，让异常继续传播。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;里面还有一些细节也挺有意思的，这里就先不讲了，有空的话再原地补坑。先这样，睡觉~&lt;/p&gt;

</description>
        <pubDate>Wed, 10 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/09/10/Contextlib-ContextManager-Source/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/09/10/Contextlib-ContextManager-Source/</guid>
        
        <category>Python</category>
        
        <category>Python进阶</category>
        
        <category>上下文管理器</category>
        
        
      </item>
    
      <item>
        <title>穷哥们的电子书及笔记多平台同步免费方案</title>
        <description>&lt;p&gt;随着电子书资源如雨后春笋般地涌现在互联网，很多人都从传统的纸质书阅读转移到电子书上。对于一些专业相关的书籍，电子书往往都是PDF格式，且需要做大量的笔记。我们往往希望这些笔记能够多端同步，可随时随地地翻阅以前的读书心得。而目前也有不少的解决方案，比如&lt;a href=&quot;https://www.marginnote.com/&quot;&gt;MarginNote&lt;/a&gt;，&lt;a href=&quot;https://www.goodnotes.com/&quot;&gt;GoodNotes&lt;/a&gt;这些，但这些应用往往需要收费，且可同步的平台往往也受限颇多。对于穷哥们而言，我们需要探索更实惠且更实用的阅读及同步方案，在不花一分钱的前提下，能够&lt;del&gt;白嫖&lt;/del&gt;享受同步的便利。&lt;/p&gt;

&lt;h2 id=&quot;使用zotero进行笔记管理及多端同步&quot;&gt;使用Zotero进行笔记管理及多端同步&lt;/h2&gt;

&lt;p&gt;一般对于苹果全家桶而言，使用&lt;a href=&quot;https://pdfexpert.com/&quot;&gt;PDF Expert&lt;/a&gt;，&lt;a href=&quot;https://www.marginnote.com/&quot;&gt;MarginNote&lt;/a&gt;这些同步PDF及笔记足矣，但这些App大多并不支持Windows，Linux平台，说不上是一个合适的多端同步方案。还好，在学术圈中，还有一个大名鼎鼎的软件——&lt;strong&gt;Zotero&lt;/strong&gt;。虽说Zotero的使用场合大多都是文献管理，但用在PDF电子书阅读上也足够合适。&lt;/p&gt;

&lt;p&gt;Zotero官网地址：&lt;a href=&quot;https://www.zotero.org/&quot;&gt;https://www.zotero.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;建议注册一个Zotero账号&lt;/strong&gt;，方便能同步自己的设置到各端。&lt;/p&gt;

&lt;h2 id=&quot;使用chrome内嵌pdf阅读器进行ocr分析&quot;&gt;使用Chrome内嵌PDF阅读器进行OCR分析&lt;/h2&gt;

&lt;p&gt;对于一些扫描版的PDF，我们没办法直接选择文本进行高亮。在过去，一般都需要借助一些专业PDF工具进行OCR扫描，这些工具往往需要收费，即便有免费使用往往也会存在额度，对于大部头而言可谓杯水车薪。还好，Chrome从2024年的&lt;a href=&quot;https://chromeos.dev/en/releases/chromeos-126&quot;&gt;126版本&lt;/a&gt;后，其内嵌PDF阅读器已经支持了&lt;strong&gt;免费的本地OCR扫描功能&lt;/strong&gt;！穷哥们的我们可以利用这个功能，不用一分钱即可轻松给扫描版的PDF文件提取文字。&lt;/p&gt;

&lt;p&gt;怎么启用这个功能呢？很简单，直接在Chrome里打开PDF文件即可。如果是扫描版PDF，此时Chrome会自动开启OCR，并在左下方会出现一个“正在从PDF文件中提取文本…”的提示。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/chrome_extracting_text_from_pdf.png&quot; alt=&quot;Chrome上“正在从PDF文件中提取文本”的提示&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;Chrome上“正在从PDF文件中提取文本”的提示&lt;/p&gt;

&lt;p&gt;耐心等待，直至提示消失，此时我们可以发现所有的页面都可以直接选择文字了。完美！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/select_text_from_pdf_in_chrome.png&quot; alt=&quot;OCR转换后，所有的页面都可以直接选择文字了&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;OCR转换后，所有的页面都可以直接选择文字了&lt;/p&gt;

&lt;p&gt;遗憾的是，Chrome并不支持将这些文本保存在原PDF中。我们需要曲线救国，使用&lt;strong&gt;打印功能&lt;/strong&gt;将文本保留下来。注意目标打印机选择“另存为PDF”，别选错成自己现实的打印机了，要不然浪费一堆纸🤪&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/print_pdf_in_chrome.png&quot; alt=&quot;使用打印功能将文本保留下来&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;使用打印功能将文本保留下来&lt;/p&gt;

&lt;p&gt;可以发现，这时候“打印”下来的PDF文件是可以选择文本的了，但遗憾的是，“打印”之后，&lt;strong&gt;书签内容丢失了😨&lt;/strong&gt;，我们还需要借助PDF工具，将书签迁移过来。&lt;/p&gt;

&lt;p&gt;还好还好，书签导入导出功能在很多PDF编辑器中是免费的（毕竟技术含量不算高，收费就过分了）。我们可以使用福昕PDF编辑器，从原来的扫描版PDF导出书签内容，然后导入到“打印”后的PDF就好啦。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/export_or_import_bookmarks_in_foxit_pdf_editor.png&quot; alt=&quot;在福昕PDF编辑器导出/导入书签内容&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;在福昕PDF编辑器导出/导入书签内容&lt;/p&gt;

&lt;p&gt;此时，我们再导入到Zotero里面，就可以轻松选择并高亮文本了。&lt;/p&gt;

&lt;h2 id=&quot;使用infini-cloud进行webdav同步&quot;&gt;使用Infini Cloud进行WebDAV同步&lt;/h2&gt;

&lt;p&gt;我们还缺最后一步，也是最关键的一步：PDF及笔记的同步。&lt;/p&gt;

&lt;p&gt;如果使用Zotero进行PDF及笔记的同步，一般有两种办法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;使用Zotero官方的云同步服务：小贵，和账号强绑定&lt;/li&gt;
  &lt;li&gt;使用WebDAV协议同步：自定义空间比较大，需要自己寻找支持WebDAV云盘&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于穷哥们而言，使用第二个方法可以省下不少银子。但国内可选的云盘并不大，大厂的云盘往往不支持WebDAV，而坚果云的价格也略贵。我们可以将目光投在国外的云盘上，看看有没有羊毛可薅。&lt;/p&gt;

&lt;p&gt;InfiniCLOUD是一个性价比较高的WebDAV云盘，注册后即可有20G的免费空间，相比坚果云的免费方案要实用不少。虽然体验下来，InfiniCLOUD网页版的速度不敢恭维，但用作Zotero WebDAV同步此类实时性要求不高的场合还是足够的。&lt;/p&gt;

&lt;p&gt;InfiniCLOUD地址：&lt;a href=&quot;https://infini-cloud.net/en/&quot;&gt;https://infini-cloud.net/en/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;注册成功后，我们可在&lt;a href=&quot;https://infini-cloud.net/en/modules/mypage/usage/&quot;&gt;My Page页面&lt;/a&gt;中，开启Apps Connection&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/turn_on_apps_connection.png&quot; alt=&quot;开启Apps Connection&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;开启Apps Connection&lt;/p&gt;

&lt;p&gt;将InfiniCLOUD提供的WebDAV地址、账号及密码填到Zotero的设置里，然后开启Sync功能即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/zotero_webdav_settings.png&quot; alt=&quot;Zotero WebDAV设置&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;Zotero WebDAV设置&lt;/p&gt;

&lt;p&gt;Enjoy Reading!&lt;/p&gt;

&lt;h2 id=&quot;附再白嫖infinicloud-5g的免费空间&quot;&gt;附：再白嫖InfiniCLOUD 5G的免费空间&lt;/h2&gt;

&lt;p&gt;可以在&lt;a href=&quot;https://infini-cloud.net/en/modules/mypage/usage/&quot;&gt;My Page&lt;/a&gt; → Referral Bonus → Enter Friends Referral Code 填我的邀请码，能再白嫖5G的免费空间🤪：K8K8Z&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/infini_cloud_referral_bonus.png&quot; alt=&quot;InfiniCLOUD推荐奖励&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;InfiniCLOUD推荐奖励&lt;/p&gt;

</description>
        <pubDate>Sat, 06 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/09/06/Poor-Guys-Sync-Pdf/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/09/06/Poor-Guys-Sync-Pdf/</guid>
        
        <category>杂七杂八</category>
        
        <category>PDF</category>
        
        <category>电子书</category>
        
        <category>同步</category>
        
        
      </item>
    
      <item>
        <title>在PyCharm中调试PyQt6程序报错&quot;This application failed to start because no Qt platform plugin could be initialized.&quot;的解决办法</title>
        <description>&lt;p&gt;在使用PyCharm开发PyQt6程序的时候，有时候调试会遇上一些比较诡异的问题。比如在macOS上，会出现以下报错：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;objc[69622]: Class QDarwinBluetoothPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x1070333f8) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac6f08). One of the two will be used. Which one is undefined.
objc[69622]: Class QDarwinCalendarPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x107033470) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac6f80). One of the two will be used. Which one is undefined.
objc[69622]: Class QDarwinCameraPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x1070334c0) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac6fd0). One of the two will be used. Which one is undefined.
objc[69622]: Class QDarwinContactsPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x107033510) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac7020). One of the two will be used. Which one is undefined.
objc[69622]: Class QDarwinMicrophonePermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x107033560) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac70c0). One of the two will be used. Which one is undefined.
qt.qpa.plugin: Could not load the Qt platform plugin &quot;cocoa&quot; in &quot;&quot; even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: offscreen, minimal, cocoa.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;同样地，在Windows上可能也会出现类似的问题：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/debug-pyqt6-error-in-pycharm/err-on-windows.png&quot; alt=&quot;Windows上类似的错误&quot; width=&quot;400&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;Windows上类似的错误&lt;/p&gt;

&lt;p&gt;看起来是缺少插件或者环境问题导致的。但是诡异的是，直接运行却不会出现上述的问题，只有PyCharm上调试才会出现。那这种大概率是PyCharm内部机制影响到运行环境了。&lt;/p&gt;

&lt;h2 id=&quot;修复方法&quot;&gt;修复方法&lt;/h2&gt;

&lt;p&gt;如果出现上述问题，可以先使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip freeze&lt;/code&gt;观察下Python环境是否同时安装了PySide6和PyQt6，如果有，那大概率是PyCharm里面的pydevd调试器的补丁代码影响到程序运行了。&lt;/p&gt;

&lt;p&gt;打开PyCharm的设置，在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Python -&amp;gt; Debugger&lt;/code&gt;面板中，&lt;strong&gt;如果&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt compatible&lt;/code&gt;已勾选，且所选项为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Auto&lt;/code&gt;，此时需要调整为程序所用的框架，即&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt6&lt;/code&gt;&lt;/strong&gt;。保存设置后，重新调试就好啦。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/debug-pyqt6-error-in-pycharm/pycharm-settings.png&quot; alt=&quot;调整PyCharm里的`PyQt compatible`设置&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;调整PyCharm里的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt compatible&lt;/code&gt;设置&lt;/p&gt;

&lt;h2 id=&quot;为什么会这样&quot;&gt;为什么会这样？&lt;/h2&gt;

&lt;p&gt;或许你会好奇为什么会出现这种情况，且为什么直接运行的时候又不会报错呢？其实这是pydevd对Qt框架注入测试代码的时候（&lt;a href=&quot;https://github.com/fabioz/PyDev.Debugger/blob/main/_pydev_bundle/pydev_monkey_qt.py#L20&quot;&gt;参见文件&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_pydev_bundle/pydev_monkey_qt&lt;/code&gt;&lt;/a&gt;），如果上述设置所选的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt compatible&lt;/code&gt;为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auto&lt;/code&gt;，那么它会根据自己的优先级寻找当前环境&lt;strong&gt;所安装&lt;/strong&gt;的目标框架（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyside6&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyside2&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyside&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyqt6&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyqt5&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyqt4&lt;/code&gt;），而非真正的”auto”（智能识别程序所用的Qt框架）。如果pydevd发现PySide6已安装，此时只会注入PySide6，而不会注入PyQt6的。&lt;/p&gt;

&lt;p&gt;而注入的过程本质是通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import&lt;/code&gt;语句导入相应的框架，然后再对其内部的对象进行猴子补丁。这就意味着，对于&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt6&lt;/code&gt;程序，它会在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt6&lt;/code&gt;导入前，先导入了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PySide6&lt;/code&gt;，相当于&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# --&amp;gt; Inserted by pydevd
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PySide6&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ... monkey patching for PySide6
# &amp;lt;-- Inserted Finished
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Here is your code...
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PyQt6&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ... your code&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;所以也就是为什么macOS里的PyCharm会抱怨&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class QDarwinBluetoothPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x1070333f8) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac6f08). One of the two will be used. Which one is undefined.&lt;/code&gt;了😅。&lt;/p&gt;

</description>
        <pubDate>Sun, 31 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/08/31/Debug-PyQt6-Error-in-PyCharm/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/08/31/Debug-PyQt6-Error-in-PyCharm/</guid>
        
        <category>杂七杂八</category>
        
        <category>Python</category>
        
        <category>PyQt</category>
        
        
      </item>
    
      <item>
        <title>macOS 15使用brew安装binutils找不到命令(gobjdump, greadelf)的解决办法</title>
        <description>&lt;p&gt;最近在看《程序员的自我修养》，想在自己的mac上试验一下ELF文件的解析，找了下教程，使用brew安装了binutils后，直接在命令行输入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gobjdump&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;greadelf&lt;/code&gt;还是找不着命令。最后搜了下，发现brew安装的二进制文件放在了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/homebrew/opt&lt;/code&gt;上了。遂直接添加到环境变量即可。&lt;/p&gt;

&lt;!--more--&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/opt/homebrew/opt/binutils/bin:&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Sat, 30 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/08/30/macOS-use-binutils/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/08/30/macOS-use-binutils/</guid>
        
        <category>杂七杂八</category>
        
        <category>超级短</category>
        
        
      </item>
    
      <item>
        <title>Cursor连接Github Codespaces札记</title>
        <description>&lt;p&gt;Cursor可谓是今年AI Agent编程的网红产品了，其本质是在VS Code的基础上新增AI相关的功能。因此，VS Code的特性和大部分插件，它基本也支持。那么，按理说它也同样支持连接到Github Codespaces上进行开发，再加上强大的AI Agent，岂不美滋滋呢！&lt;/p&gt;

&lt;p&gt;可惜的是，由于某种限制，Cursor官方并不能直接连接Github Codespaces。即便通过导入的方法安装VS Code的Github Codespaces扩展，尝试了下，也只能登录和查看Codespaces列表，但无法连接到Codespaces上进行代码编辑。只能换别的路子尝试下。&lt;/p&gt;

&lt;p&gt;经过周末的一番尝试，终于通过SSH扩展以及Github CLI成功连接上Github Codespaces了，在codespace上使用AI和在本地代码开发一样丝滑，下面简单分享一下搭建的过程。&lt;/p&gt;

&lt;h2 id=&quot;前提条件&quot;&gt;前提条件&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Cursor扩展：Remote - SSH&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;需要安装好Github命令行工具&lt;a href=&quot;https://cli.github.com/&quot;&gt;Github CLI (gh)&lt;/a&gt;，然后根据&lt;a href=&quot;https://cli.github.com/manual/&quot;&gt;官方指引&lt;/a&gt;登录Github和授权访问codespaces上的内容。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;配置过程&quot;&gt;配置过程&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;先启动&lt;/strong&gt;需要连接的codespace（可以在Github网页上启动；也可以使用Github CLI键入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh&lt;/code&gt;，根据交互式提示连接到codespace上启动，SSH连接成功后键入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt;退出即可），然后输入命令&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh --config&lt;/code&gt;生成SSH配置，不处于运行状态的codespace将被跳过（此时会输出”skiping unavaliable codespace xxx”）。如果观察到该命令有有效的输出（开头是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Host xxxx&lt;/code&gt;），则说明配置生成成功了。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/gh-codespace-config-screenshot.png&quot; alt=&quot;`gh codespace ssh --config`输出截图&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh --config&lt;/code&gt;输出截图&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;回去Cursor，键入F1拉起命令面板，输入”SSH”，进入”Remote SSH - Open SSH Configuration File…”，打开SSH配置文件。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;将第一步&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh --config&lt;/code&gt;的输出（如果出现”skiping unavaliable codespace”开头的行，不要将这些行复制过去）添加到配置文件中（在后面追加，不要覆盖原来的内容）。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/copy-config-output.png&quot; alt=&quot;将`gh codespace ssh --config`的输出复制到SSH配置文件中&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh --config&lt;/code&gt;的输出复制到SSH配置文件中&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;保存后，键入F1拉起命令面板，同样输入”SSH”，进入”Remote SSH - Connect to Host…”连接刚刚配置好的Host。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/connect-to-ssh-1.png&quot; alt=&quot;在命令面板输入SSH，进入&amp;quot;Remote SSH - Connect to Host…&amp;quot;&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;在命令面板输入SSH，进入”Remote SSH - Connect to Host…”&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/connect-to-ssh-2.png&quot; alt=&quot;找到刚刚配置好的Host进行连接&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;找到刚刚配置好的Host进行连接&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;大功告成，此时在新拉起的Cursor界面上点击”Open Folder”即可，一般而言项目目录在”/workspaces/&lt;repo name=&quot;&quot;&gt;&quot;，Let’s use cursor to code!&lt;/repo&gt;&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/open-folder-to-code.png&quot; alt=&quot;在新拉起的Cursor界面上点击&amp;quot;Open Folder&amp;quot;，打开项目所在目录&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;在新拉起的Cursor界面上点击”Open Folder”，打开项目所在目录&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

</description>
        <pubDate>Sun, 20 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/07/20/Cursor-Connect-to-Github-Codespaces/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/07/20/Cursor-Connect-to-Github-Codespaces/</guid>
        
        <category>Cursor</category>
        
        <category>Github</category>
        
        <category>Github Codespaces</category>
        
        <category>杂七杂八</category>
        
        
      </item>
    
      <item>
        <title>C++模板元编程学习笔记 1: 模板的模板参数</title>
        <description>&lt;p&gt;模板的模板参数是C++模板元编程一个重要的基础概念。它允许我们在模板中使用其他模板作为参数，从而实现更复杂的类型和行为。&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;首先看一段简单的代码示例：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;tuple&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;first_type_getter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;first_type_getter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 不能这样子写&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 这样的话，first_type需要提供不止一个模板参数的&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 原本的 first_type&amp;lt;tuple&amp;lt;int, float, double&amp;gt;&amp;gt; 就要变成 first_type&amp;lt;tuple, int, float, double&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// template&amp;lt;template&amp;lt;typename...&amp;gt; typename Ts, typename T1, typename... TN&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// using first_type = typename first_type_getter&amp;lt;Ts&amp;lt;T1, TN...&amp;gt;&amp;gt;::type;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_type_getter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_same_v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_same_v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 0&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_same_v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 0&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;在这里，我们首先定义了一个通用的模板 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type_getter&lt;/code&gt;，它接受一个类型参数。然后，我们对这个模板&lt;strong&gt;进行了偏特化&lt;/strong&gt;，通过&lt;em&gt;模板的模板参数&lt;/em&gt;使其能够深入获取&lt;strong&gt;参数内部的类型&lt;/strong&gt;。这个偏特化版本提取了模板参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ts&lt;/code&gt;中的第一个类型 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T1&lt;/code&gt;，并将其定义为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt;。在后续使用时，我们就可以通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type&amp;lt;std::tuple&amp;lt;int, float, double&amp;gt;&amp;gt;&lt;/code&gt;来获取元组的第一个类型&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;模板的模板参数语法&quot;&gt;模板的模板参数语法&lt;/h3&gt;

&lt;p&gt;需要注意的是，使用模板的模板参数时，正确的语法应该长这样 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template&amp;lt;template&amp;lt;typename...&amp;gt; typename Ts&amp;gt;&lt;/code&gt;。其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template&amp;lt;typename...&amp;gt;&lt;/code&gt;表示这个参数是一个模板，然后后面&lt;strong&gt;还需要加上&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;typename&lt;/code&gt;&lt;/strong&gt;以表明这是一个类型参数。&lt;/p&gt;

&lt;h3 id=&quot;在上述的例子中即便ts的实参是一个类型别名编译器仍然能够正确解析&quot;&gt;在上述的例子中，即便Ts的实参是一个类型别名，编译器仍然能够正确解析。&lt;/h3&gt;

&lt;p&gt;比如，如果我们使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using MyTuple = std::tuple&amp;lt;int, float, double&amp;gt;;&lt;/code&gt;，然后调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type&amp;lt;MyTuple&amp;gt;&lt;/code&gt;，编译器仍然能够正确解析出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; 作为第一个类型。&lt;/p&gt;

&lt;h3 id=&quot;为什么这里需要以偏特化的方式来实现第一个类型获取&quot;&gt;为什么这里需要以偏特化的方式来实现第一个类型获取？&lt;/h3&gt;

&lt;p&gt;我们先假定不用偏特化的形式，直接定义一个模板：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;first_type_getter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;如此，我们可以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type_getter&amp;lt;std::tuple&amp;lt;int, float, double&amp;gt;&amp;gt;&lt;/code&gt;来获取第一个类型吗？答案是否定的。因为在这种情况下，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type_getter&lt;/code&gt;的实例化&lt;strong&gt;需要提供不止一个模板参数&lt;/strong&gt;（因为形参列表有多个参数），和我们的本意相悖。由于我们只需要传一个模板实参，且目的是&lt;strong&gt;从该模板实参中提取出内部的参数&lt;/strong&gt;，所以只能&lt;strong&gt;先定义一个通用的、接受一个类型参数的模板&lt;/strong&gt;，然后再对其进行偏特化。&lt;/p&gt;
</description>
        <pubDate>Wed, 16 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/07/16/Cpp-Template-Learning-1/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/07/16/Cpp-Template-Learning-1/</guid>
        
        <category>C++</category>
        
        <category>C++进阶</category>
        
        <category>C++模板</category>
        
        <category>C++元编程</category>
        
        
      </item>
    
      <item>
        <title>Python对象__dict__方法分析札记</title>
        <description>&lt;p&gt;该文章是对Python对象&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;方法的分析笔记，未完待续…&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;1-为什么类里面的__dict__里面有一个键为__dict__的项&quot;&gt;1: 为什么类里面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;里面有一个键为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;的项？&lt;/h2&gt;

&lt;p&gt;这是因为避免了通过实例调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;的无限递归。对于类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt;的一个实例&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a.__dict__&lt;/code&gt;存储了实例&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;的属性，而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;本身也作为了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;的属性。当调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a.__dict__&lt;/code&gt;时，根据&lt;a href=&quot;https://docs.python.org/zh-cn/3.9/howto/descriptor.html#invocation-from-an-instance&quot;&gt;实例调用属性规则&lt;/a&gt;，如果&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A.__dict__[&apos;__dict__&apos;]&lt;/code&gt;不存在，或者不是一个数据描述器，那么会继而调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a.__dict__[&apos;__dict__&apos;]&lt;/code&gt;，从而陷入无限递归。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; a.__dict__
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;a&apos;&lt;/span&gt;: 8&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; A.__dict__
mappingproxy&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;__module__&apos;&lt;/span&gt;: &lt;span class=&quot;s1&quot;&gt;&apos;__main__&apos;&lt;/span&gt;, &lt;span class=&quot;s1&quot;&gt;&apos;a&apos;&lt;/span&gt;: 5, &lt;span class=&quot;s1&quot;&gt;&apos;__init__&apos;&lt;/span&gt;: &amp;lt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;A.__init__ at 0x0000022E13EA9DA0&amp;gt;, &lt;span class=&quot;s1&quot;&gt;&apos;__dict__&apos;&lt;/span&gt;: &amp;lt;attribute &lt;span class=&quot;s1&quot;&gt;&apos;__dict__&apos;&lt;/span&gt; of &lt;span class=&quot;s1&quot;&gt;&apos;A&apos;&lt;/span&gt; objects&amp;gt;, &lt;span class=&quot;s1&quot;&gt;&apos;__weakref__&apos;&lt;/span&gt;: &amp;lt;attribute &lt;span class=&quot;s1&quot;&gt;&apos;__weakref__&apos;&lt;/span&gt; of &lt;span class=&quot;s1&quot;&gt;&apos;A&apos;&lt;/span&gt; objects&amp;gt;, &lt;span class=&quot;s1&quot;&gt;&apos;__doc__&apos;&lt;/span&gt;: None&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; A.__dict__[&lt;span class=&quot;s1&quot;&gt;&apos;__dict__&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&amp;lt;attribute &lt;span class=&quot;s1&quot;&gt;&apos;__dict__&apos;&lt;/span&gt; of &lt;span class=&quot;s1&quot;&gt;&apos;A&apos;&lt;/span&gt; objects&amp;gt;

&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;A.__dict__[&lt;span class=&quot;s1&quot;&gt;&apos;__dict__&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;])&lt;/span&gt;
&amp;lt;class &lt;span class=&quot;s1&quot;&gt;&apos;getset_descriptor&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; A.__dict__[&lt;span class=&quot;s1&quot;&gt;&apos;__dict__&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;.__get__&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;a, A&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;a&apos;&lt;/span&gt;: 8&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;因此，Python在&lt;strong&gt;类中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;&lt;/strong&gt;添加了一个键为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;的项，其为一个数据描述器（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getset_descriptor&lt;/code&gt;，可参见&lt;a href=&quot;https://github.com/python/cpython/blob/main/Objects/descrobject.c&quot;&gt;CPython的源码&lt;/a&gt;），当通过实例调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;（即&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a.__dict__&lt;/code&gt;）时，解释器会优先调用这个数据描述器，从而避免了无限递归。当然，如果要覆写实例的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;属性（比如&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a.__dict__ = {}&lt;/code&gt;），也是会优先调用这个数据描述器实现覆盖，而不会造成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a.__dict__[&apos;__dict__&apos;] = {}&lt;/code&gt;这种情况。（问题来了：那实例的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;字典放在哪里了呢？）&lt;/p&gt;

&lt;p&gt;如果我想通过改写类的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;属性，删掉里面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;项，是否会造成无限递归呢？不会的，因为类的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;属性是一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mappingproxy&lt;/code&gt;对象，其本质上是一个只读的字典视图，因此无法直接修改，保证了程序的安全性。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; del A.__dict__[&lt;span class=&quot;s1&quot;&gt;&apos;__dict__&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
Traceback &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;most recent call last&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:
  File &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;stdin&amp;gt;&quot;&lt;/span&gt;, line 1, &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &amp;lt;module&amp;gt;
TypeError: &lt;span class=&quot;s1&quot;&gt;&apos;mappingproxy&apos;&lt;/span&gt; object does not support item deletion&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;2-类中的__dict__和实例中的__dict__有什么区别&quot;&gt;2. 类中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;和实例中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;有什么区别？&lt;/h2&gt;

&lt;p&gt;类中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;是一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mappingproxy&lt;/code&gt;对象，实例中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;是一个普通的字典对象。类中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;是只读的，而实例中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;是可读可写的。本质上，类的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;其实就是其超类的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__[&apos;__dict__&apos;]&lt;/code&gt;，如此类推。&lt;/p&gt;

</description>
        <pubDate>Sun, 04 May 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/05/04/Python-Object-Dict-Method/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/05/04/Python-Object-Dict-Method/</guid>
        
        <category>Python</category>
        
        <category>Python进阶</category>
        
        
      </item>
    
      <item>
        <title>Github的Python项目发布到PyPI的简单方法札记</title>
        <description>&lt;p&gt;最近在弄一个新的Python项目，想把它放在PyPI上。由于之前的项目要么需要手动在本地通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twine&lt;/code&gt;打包上传，要么就是推上到Github仓库后，再各种设置Github Action以实现自动打包上传，虽然简单了不少，但还是需要自己生成token，并将其设置在仓库的secrets里面，步骤还是有点点多。现在PyPI支持了所谓的&lt;a href=&quot;https://docs.pypi.org/trusted-publishers/&quot;&gt;“受信任发布者”（Trusted Publisher）&lt;/a&gt;，让发布流程更加的方便了。本文将以自己的新项目&lt;a href=&quot;https://github.com/JezaChen/ihook/&quot;&gt;ihook&lt;/a&gt;作为例子，简单介绍下如何快速将自己在Github上的Python项目发布到PyPI上。&lt;/p&gt;

&lt;h2 id=&quot;步骤&quot;&gt;步骤&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;不必多说，需要发布到PyPI的Python项目需要有一个构建文件（比如使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setuptools&lt;/code&gt;构建的话，就需要&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.py&lt;/code&gt;或者&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.cfg&lt;/code&gt;）。具体可以参考PyPa的&lt;a href=&quot;https://packaging.python.org/tutorials/packaging-projects/&quot;&gt;官方文档&lt;/a&gt;。我自己小项目的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.py&lt;/code&gt;大差不差，可以参考&lt;a href=&quot;https://github.com/JezaChen/ihook/blob/main/setup.py&quot;&gt;这里&lt;/a&gt;。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;注册一个PyPI账号。直接去&lt;a href=&quot;https://pypi.org/&quot;&gt;PyPI&lt;/a&gt;注册就好。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;将自己的项目推到Github的一个仓库上。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;新建一个workflow（仓库网页-&amp;gt;Actions-&amp;gt;New workflow），选择“Publish Python Package”，此时Github会生成一个workflow文件（类似&lt;a href=&quot;https://github.com/JezaChen/ihook/blob/main/.github/workflows/python-publish.yml&quot;&gt;这样&lt;/a&gt;），&lt;strong&gt;无需任何改动，直接commit&lt;/strong&gt;即可。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;进去PyPI账号设置页里面的“Publishing”选项卡（可以直接点击&lt;a href=&quot;https://pypi.org/manage/account/publishing/&quot;&gt;这里&lt;/a&gt;），在下方的Github选项卡里面输入项目名字、账号名、仓库名以及workflow文件的路径（一般是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python-publish.yml&lt;/code&gt;），然后点击“Add”即可。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/github-python-repo-to-pypi/pypi-form.png&quot; alt=&quot;PyPI项目表单填写示意&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;PyPI项目表单填写示意&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;此时，只需在Github仓库创建Release，即可自动触发workflow，将项目发布到PyPI上！&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

</description>
        <pubDate>Sat, 21 Dec 2024 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2024/12/21/Publish-Python-Packages-In-Github-To-Pypi/</link>
        <guid isPermaLink="true">https://www.jeza.net/2024/12/21/Publish-Python-Packages-In-Github-To-Pypi/</guid>
        
        <category>杂七杂八</category>
        
        <category>Python</category>
        
        <category>Github</category>
        
        
      </item>
    
  </channel>
</rss>
