-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPython import机制备忘笔记.html
309 lines (297 loc) · 20.1 KB
/
Python import机制备忘笔记.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="littlewhite" />
<meta name="copyright" content="littlewhite" />
<meta property="og:type" content="article" />
<meta name="twitter:card" content="summary">
<meta name="keywords" content="python, Language, " />
<meta property="og:title" content="Python import机制备忘笔记 "/>
<meta property="og:url" content="https://chukeer.github.io/Python import机制备忘笔记.html" />
<meta property="og:description" content="python的模块有两种组织方式,一种是单纯的python文件,文件名就是模块名,一种是包,包是一个包含了若干python文件的目录,目录下必须有一个文件__init__.py,这样目录名字就是模块名,包里的python文件也可以通过包名.文件名的方式import import语法¶ import语法有两种 直接import模块 import Module import Module as xx 从模块import对象(下级模块,类,函数,变量等) from Module import Name from Module immport Name as yy as语法是用来设置对象(这里用对象泛指模块,类,函数等等)别名,import将对象名字引入了当前文件的名字空间 假设有如下目录结构 ├── A.py └── pkg ├── B.py └── __init__ ..." />
<meta property="og:site_name" content="楚客" />
<meta property="og:article:author" content="littlewhite" />
<meta property="og:article:published_time" content="2016-04-07T00:00:00+08:00" />
<meta property="" content="2016-04-07T00:00:00+08:00" />
<meta name="twitter:title" content="Python import机制备忘笔记 ">
<meta name="twitter:description" content="python的模块有两种组织方式,一种是单纯的python文件,文件名就是模块名,一种是包,包是一个包含了若干python文件的目录,目录下必须有一个文件__init__.py,这样目录名字就是模块名,包里的python文件也可以通过包名.文件名的方式import import语法¶ import语法有两种 直接import模块 import Module import Module as xx 从模块import对象(下级模块,类,函数,变量等) from Module import Name from Module immport Name as yy as语法是用来设置对象(这里用对象泛指模块,类,函数等等)别名,import将对象名字引入了当前文件的名字空间 假设有如下目录结构 ├── A.py └── pkg ├── B.py └── __init__ ...">
<title>Python import机制备忘笔记 · 楚客
</title>
<!--
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.1/css/font-awesome.css" rel="stylesheet">
--!>
<link href="https://chukeer.github.io/theme/css/bootstrap-combined.min.css" rel="stylesheet">
<link href="https://chukeer.github.io/theme/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="https://chukeer.github.io/theme/css/pygments.css" media="screen">
<link rel="stylesheet" type="text/css" href="https://chukeer.github.io/theme/tipuesearch/tipuesearch.css" media="screen">
<link rel="stylesheet" type="text/css" href="https://chukeer.github.io/theme/css/elegant.css" media="screen">
<link rel="stylesheet" type="text/css" href="https://chukeer.github.io/theme/css/custom.css" media="screen">
</head>
<body>
<div id="content-sans-footer">
<div class="navbar navbar-static-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="https://chukeer.github.io/"><span class=site-name>楚客</span></a>
<div class="nav-collapse collapse">
<ul class="nav pull-right top-menu">
<li ><a href="https://chukeer.github.io">Home</a></li>
<li ><a href="https://chukeer.github.io/categories.html">Categories</a></li>
<li ><a href="https://chukeer.github.io/tags.html">Tags</a></li>
<li ><a href="https://chukeer.github.io/archives.html">Archives</a></li>
<li><form class="navbar-search" action="https://chukeer.github.io/search.html" onsubmit="return validateForm(this.elements['q'].value);"> <input type="text" class="search-query" placeholder="Search" name="q" id="tipue_search_input"></form></li>
</ul>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span1"></div>
<div class="span10">
<article>
<div class="row-fluid">
<header class="page-header span10 offset2">
<h1><a href="https://chukeer.github.io/Python import机制备忘笔记.html"> Python import机制备忘笔记 </a></h1>
</header>
</div>
<div class="row-fluid">
<div class="span2 table-of-content">
<nav>
<h4>Contents</h4>
<div class="toc">
<ul>
<li><a href="#import">import语法</a></li>
<li><a href="#import_1">import步骤</a></li>
<li><a href="#import_2">嵌套import</a></li>
<li><a href="#import_3">包的import</a></li>
<li><a href="#_1">相对导入和绝对导入</a></li>
</ul>
</div>
</nav>
</div>
<div class="span8 article-content">
<p>python的模块有两种组织方式,一种是单纯的python文件,文件名就是模块名,一种是包,包是一个包含了若干python文件的目录,目录下必须有一个文件<code>__init__.py</code>,这样目录名字就是模块名,包里的python文件也可以通过<code>包名.文件名</code>的方式import</p>
<h3 id="import">import语法<a class="headerlink" href="#import" title="Permanent link">¶</a></h3>
<p>import语法有两种</p>
<ol>
<li>
<p>直接import模块</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">Module</span>
<span class="kn">import</span> <span class="nn">Module</span> <span class="kn">as</span> <span class="nn">xx</span>
</pre></div>
</li>
<li>
<p>从模块import对象(下级模块,类,函数,变量等)</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">Module</span> <span class="kn">import</span> <span class="n">Name</span>
<span class="kn">from</span> <span class="nn">Module</span> <span class="nn">immport</span> <span class="nn">Name</span> <span class="nn">as</span> <span class="nn">yy</span>
</pre></div>
</li>
</ol>
<p>as语法是用来设置对象(这里用对象泛指模块,类,函数等等)别名,import将对象名字引入了当前文件的名字空间</p>
<p>假设有如下目录结构</p>
<div class="highlight"><pre><span></span>├── A.py
└── pkg
├── B.py
└── __init__.py
</pre></div>
<p>在当前目录下,以下语句都是有效的</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">A</span>
<span class="kn">import</span> <span class="nn">pkg</span>
<span class="kn">import</span> <span class="nn">pkg.B</span>
<span class="kn">from</span> <span class="nn">pkg</span> <span class="kn">import</span> <span class="n">B</span>
</pre></div>
<p>为了简化讨论,下面将不会对as语法进行举例</p>
<h3 id="import_1">import步骤<a class="headerlink" href="#import_1" title="Permanent link">¶</a></h3>
<p>python所有加载的模块信息都存放在sys.modules结构中,当import一个模块时,会按如下步骤来进行</p>
<ol>
<li>如果是<code>import A</code>,检查sys.modules中是否已经有A,如果有则不加载,如果没有则为A创建module对象,并加载A</li>
<li>如果是<code>from A import B</code>,先为A创建module对象,再解析A,从中寻找B并填充到A的<code>__dict__</code>中</li>
</ol>
<h3 id="import_2">嵌套import<a class="headerlink" href="#import_2" title="Permanent link">¶</a></h3>
<p>在import模块时我们可能会担心一个模块会不会被import多次,假设有A,B,C三个模块,A需要import B和C,B又要import C,这样A会执行到两次import C,一次是自己本身import,一次是在import B时执行的import,但根据上面讲到的import步骤,在第二次import时发现模块已经被加载,所以不会重复import</p>
<p>但如下情况却会报错</p>
<div class="highlight"><pre><span></span><span class="c1">#filename: A.py</span>
<span class="kn">from</span> <span class="nn">B</span> <span class="kn">import</span> <span class="n">BB</span>
<span class="k">class</span> <span class="nc">AA</span><span class="p">:</span><span class="k">pass</span>
<span class="c1">#filename: B.py</span>
<span class="kn">from</span> <span class="nn">A</span> <span class="kn">import</span> <span class="n">AA</span>
<span class="k">class</span> <span class="nc">BB</span><span class="p">:</span><span class="k">pass</span>
</pre></div>
<p>这时不管是执行A.py还是B.py都会抛出ImportError的异常,假设我们执行的是A.py,究其原因如下</p>
<ol>
<li>文件A.py执行<code>from B import BB</code>,会先扫描B.py,同时在A的名字空间中为B创建module对象,试图从B中查找BB</li>
<li>扫描B.py第一行执行<code>from A import AA</code>,此时又会去扫描A.py</li>
<li>扫描A.py第一行执行<code>from B import BB</code>,由于步骤1已经为B创建module对象,所以会直接从B的module对象的<code>__dict__</code>中获取BB,此时显然BB是获取不到的,于是抛出异常</li>
</ol>
<p>解决这种情况有两种办法,</p>
<ol>
<li>将<code>from B import BB</code>改为<code>import B</code>,或将<code>from A import AA</code>改为<code>import A</code></li>
<li>将A.py或B.py中的两行代码交换位置</li>
</ol>
<p>总之,import需要注意的是,尽量在需要用到时再import</p>
<h3 id="import_3">包的import<a class="headerlink" href="#import_3" title="Permanent link">¶</a></h3>
<p>当一个目录下有<code>__init__.py</code>文件时,该目录就是一个python的包 </p>
<p>import包和import单个文件是一样的,我们可以这样类比: </p>
<blockquote>
<p>import单个文件时,文件里的类,函数,变量都可以作为import的对象<br/>
import包时,包里的子包,文件,以及__init__.py里的类,函数,变量都可以作为import的对象</p>
</blockquote>
<p>假设有如下目录结构</p>
<div class="highlight"><pre><span></span>pkg
├── __init__.py
└── file.py
</pre></div>
<p>其中<code>__init__.py</code>内容如下</p>
<div class="highlight"><pre><span></span>argument = 0
class A:pass
</pre></div>
<p>在和pkg同级目录下执行如下语句都是OK的</p>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="kn">import</span> <span class="nn">pkg</span>
<span class="o">>>></span> <span class="kn">import</span> <span class="nn">pkg.file</span>
<span class="o">>>></span> <span class="kn">from</span> <span class="nn">pkg</span> <span class="kn">import</span> <span class="nb">file</span>
<span class="o">>>></span> <span class="kn">from</span> <span class="nn">pkg</span> <span class="kn">import</span> <span class="n">A</span>
<span class="o">>>></span> <span class="kn">from</span> <span class="nn">pkg</span> <span class="kn">import</span> <span class="n">argument</span>
</pre></div>
<p>但如下语句是错误的</p>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="kn">import</span> <span class="nn">pkg.A</span>
<span class="o">>>></span> <span class="kn">import</span> <span class="nn">pkg.argument</span>
</pre></div>
<p>报错<code>ImportError: No module named xxx</code>,因为当我们执行<code>import A.B</code>,A和B都必须是模块(文件或包)</p>
<h3 id="_1">相对导入和绝对导入<a class="headerlink" href="#_1" title="Permanent link">¶</a></h3>
<p>绝对导入的格式为<code>import A.B</code>或<code>from A import B</code>,相对导入格式为<code>from . import B</code>或<code>from ..A import B</code>,<code>.</code>代表当前模块,<code>..</code>代表上层模块,<code>...</code>代表上上层模块,依次类推。当我们有多个包时,就可能有需求从一个包import另一个包的内容,这就会产生绝对导入,而这也往往是最容易发生错误的时候,还是以具体例子来说明</p>
<p>目录结构如下</p>
<div class="highlight"><pre><span></span>app
├── __inti__.py
├── mod1
│ ├── file1.py
│ └── __init__.py
├── mod2
│ ├── file2.py
│ └── __init__.py
└── start.py
</pre></div>
<p>其中app/start.py内容为<code>import mod1.file1</code> <br/>
app/mod1/file1.py内容为<code>from ..mod2 import file2</code></p>
<p>为了便于分析,我们在所有py文件(包括<code>__init__.py</code>)第一行加入<code>print __file__, __name__</code></p>
<p>现在app/mod1/file1.py里用到了相对导入,我们在app/mod1下执行<code>python file1.py</code>或者在app下执行<code>python mod1/file1.py</code>都会报错<strong><font color="red"><code>ValueError: Attempted relative import in non-package</code></font></strong></p>
<p>在app下执行<code>python -m mod1.file1</code>或<code>python start.py</code>都会报错<strong><font color="red"><code>ValueError: Attempted relative import beyond toplevel package</code></font></strong></p>
<p>具体原因后面再说,我们先来看一下导入模块时的一些规则</p>
<p>在没有明确指定包结构的情况下,python是根据<code>__name__</code>来决定一个模块在包中的结构的,如果是<code>__main__</code>则它本身是顶层模块,没有包结构,如果是<code>A.B.C</code>结构,那么顶层模块是A。基本上遵循这样的原则</p>
<ul>
<li>如果是绝对导入,<strong>一个模块只能导入自身的子模块或和它的顶层模块同级别的模块及其子模块</strong></li>
<li>如果是相对导入,<strong>一个模块必须有包结构且只能导入它的顶层模块内部的模块</strong></li>
</ul>
<p>有目录结构如下</p>
<div class="highlight"><pre><span></span>A
├── B1
│ ├── C1
│ │ └── file.py
│ └── C2
└── B2
</pre></div>
<p>其中A,B1,B2,C1,C2都为包,这里为了展示简单没有列出<code>__init__.py</code>文件,当file.py的包结构为<code>A.B1.C1.file</code>(注意,是根据<code>__name__</code>来的,而不是磁盘的目录结构,在不同目录下执行file.py时对应的包目录结构都是不一样的)时,在file.py中可采用如下的绝对的导入</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">A.B1.C2</span>
<span class="kn">import</span> <span class="nn">A.B2</span>
</pre></div>
<p>和如下的相对导入</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">C2</span>
<span class="kn">from</span> <span class="nn">...</span> <span class="kn">import</span> <span class="n">B2</span>
</pre></div>
<p>什么情况下会让file.py的包结构为<code>A.B1.C1.file</code>呢,有如下两种</p>
<ol>
<li>在A的上层目录执行<code>python -m A.B1.C1.file</code>, 此时明确指定了包结构</li>
<li>在A的上层目录建立文件start.py,在start.py里有<code>import A.B1.C1.file</code>,然后执行<code>python start.py</code>,此时包结构是根据file.py的<code>__name__</code>变量来的</li>
</ol>
<p>再看前面出错的两种情况,第一种执行<code>python file1.py</code>和<code>python mod1/file1.py</code>,此时file.py的<code>__name__</code>为<code>__main__</code>,也就是说它本身就是顶层模块,并没有包结构,所以会报错</p>
<p>第二种情况,在执行<code>python -m mod1.file1</code>和<code>python start.py</code>时,前者明确告诉解释器mod1是顶层模块,后者需要导入file1,而file1.py的<code>__name__</code>为mod1.file1,顶层模块为也mod1,所以在file1.py中执行<code>from ..mod2 import file2</code>时会报错 ,因为mod2并不在顶层模块mod1内部。通过错误堆栈可以看出,并不是在start.py中绝对导入时报错,而是在file1.py中相对导入报的错</p>
<p>那么如何才能偶正确执行呢,有两种方法,一种是在app上层目录执行python -m app.mod1.file1,另一种是改变目录结构,将所有包放在一个大包中,如下</p>
<div class="highlight"><pre><span></span>app
├── pkg
│ ├── __init__.py
│ ├── mod1
│ │ ├── __init__.py
│ │ └── file1.py
│ └── mod2
│ ├── __init__.py
│ └── file2.py
└── start.py
</pre></div>
<p>start.py内容改成<code>import pkg.mod1.file1</code>,然后在app下执行<code>python start.py</code></p>
<hr/>
<aside>
<nav>
<ul class="articles-timeline">
<li class="previous-article">« <a href="https://chukeer.github.io/curl参数说明.html" title="Previous: curl参数说明">curl参数说明</a></li>
<li class="next-article"><a href="https://chukeer.github.io/C++ and Python 多线程笔记.html" title="Next: C++ & Python 多线程笔记">C++ & Python 多线程笔记</a> »</li>
</ul>
</nav>
</aside>
</div>
<section>
<div class="span2" style="float:right;font-size:0.9em;">
<table class="table">
<!-- <time pubdate="pubdate" datetime="2016-04-07T00:00:00+08:00"> 4 7, 2016</time> -->
<tr>
<td>Published</td>
<td><time pubdate="pubdate" datetime="2016-04-07T00:00:00+08:00">2016- 4-7</time></td>
</tr>
<tr>
<td>Category</td>
<td><a class="category-link" href="https://chukeer.github.io/categories.html#language-ref">Language</a></td>
</tr>
<tr>
<td>Tags</td>
<td>
<ul class="list-of-tags tags-in-article">
<li><a href="https://chukeer.github.io/tags.html#python-ref">python
<span>6</span>
</a></li>
</ul>
</td>
</tr>
</table>
</div>
</section>
</div>
</article>
</div>
<div class="span1"></div>
</div>
</div>
<div id="push"></div>
</div>
<footer>
<div id="footer">
<ul class="footer-content">
<li class="elegant-power">Powered by <a href="http://getpelican.com/" title="Pelican Home Page">Pelican</a>. Theme: <a href="http://oncrashreboot.com/pelican-elegant" title="Theme Elegant Home Page">Elegant</a> by <a href="http://oncrashreboot.com" title="Talha Mansoor Home Page">Talha Mansoor</a></li>
</ul>
</div>
</footer> <!--
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
-->
<script src="https://chukeer.github.io/theme/js/jquery.min.js"></script>
<script src="https://chukeer.github.io/theme/js/bootstrap.min.js"></script>
<script>
function validateForm(query)
{
return (query.length > 0);
}
</script>
<script>
$("div.article-content table").addClass("table table-hover");
</script>
</body>
<!-- Theme: Elegant built for Pelican
License : http://oncrashreboot.com/pelican-elegant -->
</html>