彻底搞懂Python 中的 import 与 from import
<p>对不少 Python 初学者来说,Python 导入其他模块的方式让他们很难理解。什么时候用<code>import xxx</code>?什么时候用<code>from xxx import yyy</code>?什么时候用<code>from xxx.yyy import zzz</code>?什么时候用<code>from xxx import *</code>?</p><p>这篇文章,我们来彻底搞懂这个问题。</p>
<h2 id="系统自带的模块">系统自带的模块</h2>
<p>以正则表达式模块为例,我们经常这样写代码:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
<div class="line">4</div>
</td>
<td class="code">
<div class="line"><span class="keyword">import re</span></div>
<div class="line"> </div>
<div class="line">target = <span class="string">'abc1234xyz'</span></div>
<div class="line">re.search(<span class="string">'(\d+)', target)</span></div>
</td>
</tr>
</tbody>
</table>
<p>但有时候,你可能会看到某些人这样写代码:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
</td>
<td class="code">
<div class="line"><span class="keyword">from re <span class="keyword">import search</span></span></div>
<div class="line">target = <span class="string">'abc1234xyz'</span></div>
<div class="line">search(<span class="string">'(\d+)', target)</span></div>
</td>
</tr>
</tbody>
</table>
<p>那么这两种导入方式有什么区别呢?</p>
<p>我们分别使用<code>type</code>函数来看看他们的类型:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
<div class="line">4</div>
<div class="line">5</div>
<div class="line">6</div>
</td>
<td class="code">
<div class="line">>>> import re</div>
<div class="line">>>> type(re)</div>
<div class="line"><class 'module'></div>
<div class="line">>>> from re import search</div>
<div class="line">>>> type(search)</div>
<div class="line"><class 'function'></div>
</td>
</tr>
</tbody>
</table>
<p>如下图所示:</p>
<p><img src="https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-12-28-13-30-59.png" alt=""></p>
<p>可以看到,直接使用<code>import re</code>导入的<code>re</code>它是一个<code>module</code>类,也就是模块。我们把它成为<code>正则表达式模块</code>。而当我们<code>from re import search</code>时,这个<code>search</code>是一个<code>function</code>类,我们称呼它为<code>search 函数</code>。</p>
<p>一个模块里面可以包含多个函数。</p>
<p>如果在你的代码里面,你已经确定只使用<code>search</code>函数,不会再使用正则表达式里面的其他函数了,那么你使用两种方法都可以,没什么区别。</p>
<p>但是,如果你要使用正则表达式下面的多个函数,或者是一些常量,那么用第一种方案会更加简洁清晰。</p>
<p>例如:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
<div class="line">4</div>
</td>
<td class="code">
<div class="line"><span class="keyword">import re</span></div>
<div class="line"> </div>
<div class="line">re.search(<span class="string">'c(.*?)x', flags=re.S)</span></div>
<div class="line">re.sub(<span class="string">'', <span class="string">'***', target, flags=re.I)</span></span></div>
</td>
</tr>
</tbody>
</table>
<p>在这个例子中,你分别使用了<code>re.search</code>,<code>re.sub</code>,<code>re.S</code>和<code>re.I</code>。后两者是常量,用于忽略换行符和大小写。</p>
<p>但是,如果你使用<code>from re import search, sub, S, I</code>来写代码,那么代码就会变成这样:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
<div class="line">4</div>
</td>
<td class="code">
<div class="line"><span class="keyword">import re</span></div>
<div class="line"> </div>
<div class="line">search(<span class="string">'c(.*?)x', flags=S)</span></div>
<div class="line">sub(<span class="string">'', <span class="string">'***', target, flags=I)</span></span></div>
</td>
</tr>
</tbody>
</table>
<p>看起来虽然简洁了,但是,一旦你的代码行数多了以后,你很容易忘记<code>S</code>和<code>I</code>这两个变量是什么东西。而且我们自己定义的函数,也很有可能取名为<code>sub</code>或者<code>search</code>,从而覆盖正则表达式模块下面的这两个同名函数。这就会导致很多难以觉察的潜在 bug。</p>
<p>再举一个例子。Python 的 <code>datetime</code>模块,我们可以直接<code>import datetime</code>,此时我们导入的是一个<code>datetime</code>模块,如下图所示:</p>
<p><img src="https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-12-28-13-43-21.png" alt=""></p>
<p>但是如果你写为<code>from datetime import datetime</code>,那么你导入的<code>datetime</code>是一个<code>type</code>类:</p>
<p><img src="https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-12-28-13-45-07.png" alt=""></p>
<p>因为这种方式导入的<code>datetime</code>,它就是Python 中的一种类型,用于表示包含日期和时间的数据。</p>
<p>这两种导入方式导入的<code>datetime</code>,虽然名字一样,但是他们的意义完全不一样,请大家观察下面两种写法:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
<div class="line">4</div>
</td>
<td class="code">
<div class="line"><span class="keyword">import datetime</span></div>
<div class="line"> </div>
<div class="line">now = datetime.datetime.now()</div>
<div class="line">one_hour_ago = now - datetime.timedelta(hours=<span class="number">1)</span></div>
</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
</td>
<td class="code">
<div class="line"><span class="keyword">from datetime <span class="keyword">import datetime, timedelta</span></span></div>
<div class="line">now = datetime.now()</div>
<div class="line">one_hour_ago = now - timedelta(hours=<span class="number">1)</span></div>
</td>
</tr>
</tbody>
</table>
<p>第二种写法看似简单,但实则改动起来却更为麻烦。例如我还需要增加一个变量<code>today</code>用于记录今日的日期。</p>
<p>对于第一段代码,我们只需要增加一行即可:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
</td>
<td class="code">
<div class="line">today = datetime.date.today()</div>
</td>
</tr>
</tbody>
</table>
<p>但对于第二行来说,我们需要首先修改导入部分的代码:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
</td>
<td class="code">
<div class="line"><span class="keyword">from datetime <span class="keyword">import datetime, timedelta, date</span></span></div>
</td>
</tr>
</tbody>
</table>
<p>然后才能改代码:<code>today = date.today()</code></p>
<p>这样一来你就要修改两个地方,反倒增加了负担。</p>
<h2 id="第三方库">第三方库</h2>
<p>在使用某些第三方库的代码里面,我们会看到类似这样的写法:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
</td>
<td class="code">
<div class="line"><span class="keyword">from lxml.html <span class="keyword">import fromstring</span></span></div>
<div class="line"> </div>
<div class="line">selector = fromstring(HTML)</div>
</td>
</tr>
</tbody>
</table>
<p>但是我们还可以写为:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
</td>
<td class="code">
<div class="line"><span class="keyword">from lxml <span class="keyword">import html</span></span></div>
<div class="line"> </div>
<div class="line">selector = html.fromstring(HTML)</div>
</td>
</tr>
</tbody>
</table>
<p>但是,下面这种写法会导致报错:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
</td>
<td class="code">
<div class="line"><span class="keyword">import lxml</span></div>
<div class="line">selector = lxml.html.fromstring(HTML)</div>
</td>
</tr>
</tbody>
</table>
<p>那么这里的<code>lxml.html</code>又是什么东西呢?</p>
<p>这种情况多常见于一些特别大型的第三方库中,这种库能处理多种类型的数据。例如<code>lxml</code>它既能处理<code>xml</code>的数据,又能处理<code>html</code>的数据,于是这种库会划分子模块,<code>lxml.html</code>模块专门负责<code>html</code>相关的数据。</p>
<h2 id="自己来实现多种导入方法">自己来实现多种导入方法</h2>
<p>我们现在自己来写代码,实现这多种导入方法。</p>
<p>我们创建一个文件夹<code>DocParser</code>,在里面分别创建两个文件<code>main.py</code>和<code>util.py</code>,他们的内容如下:</p>
<p><code>util.py</code>文件:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
</td>
<td class="code">
<div class="line"><span class="function"><span class="keyword">def <span class="title">write<span class="params">():</span></span></span></span></div>
<div class="line">print(<span class="string">'write 函数被调用!')</span></div>
</td>
</tr>
</tbody>
</table>
<p><code>main.py</code>文件:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
</td>
<td class="code">
<div class="line"><span class="keyword">import util</span></div>
<div class="line"> </div>
<div class="line">util.write()</div>
</td>
</tr>
</tbody>
</table>
<p>运行效果如下图所示:</p>
<p><img src="https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-12-28-14-06-29.png" alt=""></p>
<p>现在我们把<code>main.py</code>的导入方式修改一下:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
</td>
<td class="code">
<div class="line"><span class="keyword">from util <span class="keyword">import write</span></span></div>
<div class="line"> </div>
<div class="line">write()</div>
</td>
</tr>
</tbody>
</table>
<p>依然正常运行,如下图所示</p>
<p><img src="https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-12-28-14-07-21.png" alt=""></p>
<blockquote>
<p>当两个文件在同一个文件夹下面,并且该文件夹里面没有<strong>init</strong>.py 文件时,两种导入方式等价。</p>
</blockquote>
<p>现在,我们来创建一个文件夹<code>microsoft</code>,里面再添加一个文件<code>parse.py</code>:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
</td>
<td class="code">
<div class="line"><span class="function"><span class="keyword">def <span class="title">read<span class="params">():</span></span></span></span></div>
<div class="line">print(<span class="string">'我是 microsoft 文件夹下面的 parse.py 中的 read函数')</span></div>
</td>
</tr>
</tbody>
</table>
<p>如下图所示:</p>
<p><img src="https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-12-28-14-11-17.png" alt=""></p>
<p>此时我们在 <code>main.py</code>中对它进行调用:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
</td>
<td class="code">
<div class="line"><span class="keyword">from microsoft <span class="keyword">import parse</span></span></div>
<div class="line"> </div>
<div class="line">parse.read()</div>
</td>
</tr>
</tbody>
</table>
<p>运行效果如下图所示:</p>
<p><img src="https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-12-28-14-12-17.png" alt=""></p>
<p>我们也可以用另一种方法:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
<div class="line">3</div>
</td>
<td class="code">
<div class="line"><span class="keyword">from microsoft.parse <span class="keyword">import read</span></span></div>
<div class="line"> </div>
<div class="line">read()</div>
</td>
</tr>
</tbody>
</table>
<p>运行效果如下图所示:</p>
<p><img src="https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-12-28-14-13-17.png" alt=""></p>
<p>但是,你不能直接导入<code>microsoft</code>,如下图所示:</p>
<p><img src="https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-12-28-14-15-35.png" alt=""></p>
<blockquote>
<p><span style="color: rgba(255, 0, 0, 1)">你只能导入一个模块或者导入一个函数或者类,你不能导入一个文件夹</span></p>
</blockquote>
<p>无论你使用的是<code>import xxx</code>还是<code>from xxx.yyy.zzz.www import qqq</code>,你导入进来的东西,要不就是一个模块(对应到.py 文件的文件名),或者是某个.py 文件中的函数名、类名、变量名。</p>
<p><span style="color: rgba(255, 0, 0, 1)">无论是<code>import xxx</code>还是<code>from xxx import yyy</code>,你导入进来的都不能是一个文件夹的名字。</span></p>
<p>可能有这样一种情况,就是某个函数名与文件的名字相同,例如:</p>
<p>在 <code>microsoft</code>文件夹里面有一个<code>microsoft.py</code>文件,这个文件里面有一个函数叫做<code>microsoft</code>,那么你的代码可以写为:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<div class="line">1</div>
<div class="line">2</div>
</td>
<td class="code">
<div class="line"><span class="keyword">from microsoft <span class="keyword">import microsoft`</span></span></div>
<div class="line">microsoft.microsoft()</div>
</td>
</tr>
</tbody>
</table>
<p>但请注意分辨,这里你导入的还是模块,只不过<code>microsoft.py</code>文件名与它所在的文件夹名恰好相同而已。</p>
<h2 id="总结">总结</h2>
<p>无论是使用<code>import</code>还是<code>from import</code>,第一个要求是代码能够正常运行,其次,根据代码维护性,团队编码风格来确定选择哪一种方案。</p>
<p>如果我们只会使用到某个模块下面的一个函数(或者常量、类)并且名字不会产生混淆,可识别性高,那么<code>from 模块名 import 函数名</code>这没有什么问题。</p>
<p>如果我们会用到一个模块下面的多个函数,或者是我们将要使用的函数名、常量名、类名可能会让人产生混淆(例如 re.S、re.I),那么这种情况下,<code>import 模块名</code>然后再 <code>模块名.xxx</code>来调用会让代码更加清晰,更好维护。</p>
<p>但无论什么情况下,都禁止使用<code>from xxx import *</code>这种写法,它会给你带来无穷无尽的噩梦。</p>
<h2 id="未完待续">未完待续</h2>
<p>在明天的文章中,我们来讲讲还有一种写法<code>from . import xxx</code>,以及当文件夹中存在<code>__init__.py</code>时,导入方式又有什么变化。</p>
<p> </p>
<p> </p>
<p>转自:微信公众号:未闻code</p><br><br>
来源:https://www.cnblogs.com/tjp40922/p/13052553.html
頁:
[1]