概述
Python 是一门优美简单、功能强大的动态语言。在刚刚接触这门语言时,我们会被其优美的格式、简洁的语法和无穷无尽的类库所震撼。在真正的将python应用到实际的项目中,你会遇到一些无法避免的问题。最让人困惑不解的问题有二类,一个 编码问题,另一个则是引用问题。
本文主要讨论关于Python中import的机制与实现、以及介绍一些有意思的Python Hooks。
Python 类库引入机制
首先,看一个简单的例子:
1
2
3
4
5
6
7
8
9
10
11
|
""
"
目录结构如下:
├── __init__.py
├── main.py
└── string.py
"
""
# main.py 内容如下
import
string
print
string
.
a
# string.py 内容如下
a
=
2
|
现在,考虑一下:
- 当我们执行main.py的时候,会发生什么事情?
- 在main.py文件执行到 import string 的时候,解释器导入的string类库是当前文件夹下的string.py还是系统标准库的string.py呢?
- 如果明确的指明⾃己要引⼊的类库?
为了搞清楚上面的问题,我们需要了解关于Python类库引入的机制。
Python的两种引入机制
Python 提供了二种引入机制:
- relative import
- absolute import
relative import
relative import 也叫作相对引入,在Python2.5及之前是默认的引入方法。它的使用方法如下:
1
2
3
|
from
.
string
import
a
from
.
.
string
import
a
from
.
.
.
string
import
a
|
这种引入方式使用一个点号来标识引入类库的精确位置。与linux的相对路径表示相似,一个点表示当前目录,每多一个点号则代表向上一层目录。
1
2
3
4
5
6
7
8
9
10
11
|
""
"
├── __init__.py
├── foo.py
└── main.py
"
""
# foo.py
a
=
2
# main.py
print
__name__
from
.
foo
import
a
print
a
|
相对引入,那么我们需要知道相对什么来引入。相对引入使用被引入文件的 __name__ 属性来决定该文件在整个包结构的位置。那么如果文件的__name__没有包含任何包的信息,例如 __name__ 被设置为了__main__,则认为其为‘top level script’,而不管该文件的位置,这个时候相对引入就没有引入的参考物。如上面的程序所示,当我们执行 python main . py 时,Python解释器会抛出 ValueError: Attempted relative import in non-package的异常。
为了解决这个问题, PEP 0366 — Main module explicit relative imports提出了一个解决方案。允许用户使用python -m ex2.main的方式,来执行该文件。在这个方案下,引入了一个新的属性__package__。
1
2
3
4
5
6
7
8
9
10
11
|
liuchang
@
localhost
~
/
Codes
/
pycon
$
cat
ex2
/
main
.
py
print
__name__
print
__package__
from
.
foo
import
a
print
a
liuchang
@
localhost
~
/
Codes
/
pycon
$
python
-
m
ex2
.
main
__main__
ex2
2
|
absolute import
absolute import 也叫作完全引入,非常类似于Java的引入进制,在Python2.5被完全实现,但是是需要通过 from __future__ import absolute_import 来打开该引入进制。在Python2.6之后以及Python3,完全引用成为Python的默认的引入机制。它的使用方法如下:
1
2
|
from
pkg
import
foo
from
pkg
.
moduleA
import
foo
|
要注意的是,需要从包目录最顶层目录依次写下,而不能从中间开始。
在使用该引入方式时,我们碰到比较多的问题就是因为位置原因,Python找不到相应的库文件,抛出ImportError的异常。让我们看一个完全引用的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
""
"
ex3
├── __init__.py
├── foo.py
└── main.py
"
""
# foo.py
a
=
2
# main.py
print
__name__
print
__package__
from
ex2
.
foo
import
a
print
a
|
我们尝试着去运行main.py文件,Python解释器会抛出ImportError。那么我们如何解决这个问题呢?
1
2
3
4
5
6
7
|
$
python
ex3
/
main
.
py
__main__
None
Traceback
(
most
recent
call
last
)
:
File
"ex3/main.py"
,
line
3
,
in
<
module
>
from
ex2
.
foo
import
a
ImportError
:
No
module
named
ex2
.
foo
|
首先,我们也可以使用前文所述的module的方式去运行程序,通过-m参数来告诉解释器 __package__ 属性。如下:
1
2
3
4
5
|
liuchang
@
liuchangdeMacBook
-
Pro
~
/
Codes
/
pycon
$
python
-
m
ex3
.
main
__main__
ex3
2
|
另外,我们还有一个办法可以解决该问题,在描述之前,我们介绍一个关于Python的非常有用的小知识: Python解释器会自动将当前工作目录添加到PYTHONPATH。如下所示,可以看到我们打印出的 sys . path 已经包含了当前工作目录。
1
2
3
4
5
6
7
|
liuchang
@
liuchangdeMacBook
-
Pro
~
/
Codes
/
pycon
/
ex4
$
cat
main
.
py
import
sys
print
sys
.
path
liuchang
@
liuchangdeMacBook
-
Pro
~
/
Codes
/
pycon
/
ex4
$
python
main
.
py
[
'/Users/liuchang/Codes/pycon/ex4'
,
'/Library/Python/2.7/site-packages/pip-7.1.0-py2.7.egg'
,
'/Library/Python/2.7/site-packages/mesos-_PACKAGE_VERSION_-py2.7.egg'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload'
,
'/Users/liuchang/Library/Python/2.7/lib/python/site-packages'
,
'/usr/local/lib/python2.7/site-packages'
,
'/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC'
,
'/Library/Python/2.7/site-packages'
]
|
了解了Python解释器的这个特性后,我们就可以解决完全引用的找不到类库的问题:执行的时候,让解释器自动的将类库的目录添加到PYTHONPATH中。
我们可以在顶层目录中添加一个run_ex3.py的文件,文件内容和运行结果如下,可以看到Python解释器正确的执行了ex3.main文件。
1
2
3
4
5
6
7
8
|
liuchang
@
liuchangdeMacBook
-
Pro
~
/
Codes
/
pycon
$
cat
run_ex3
.
py
from
ex3
import
main
liuchang
@
liuchangdeMacBook
-
Pro
~
/
Codes
/
pycon
$
python
run_ex3
.
py
ex3
.
main
None
2
|
===========================================
Python import语句导入模块语法
python中的import语句是用来导入模块的,在
python模块库中有着大量的模块可供使用,要想使用这些文件需要用import语句把指定模块导入到当前程序中。
# 在学习python的朋友们,强烈推荐加入
Python QQ群。
import语句作用
import语句作用就是用来导入模块的, 它可以出现在程序中的任何位置。
import语句语法
使用import语句导入模块,import语句语法如下:
import module
关键字 模块名
使用方法例如:
import math #入导math模块
math.floor() #调用math模块中的floor()
函数
如果要同时导入多个模块,只需要在模块名之前用逗号进行分隔:
import module1,module2,module3.......
同时导入多个模块的方法,对于初学者来说可读性和可理解性不如第一种好。所以想要导入多个模块时,还是比较推荐用第一种方式,把每一个模块都单独进行一次导入,可能会感觉操作起来麻烦一些,但便于理解。
import语句导入模块顺序
在编写代码过程中,我们可能需要多种多样的模块,需要注意的是最好把导入模块放在代码的开头。
为什么要把import导入模块放在程序开头使用呢?
解释器在执行语句时,遵循作用域原则。因为这和作用域有关系,如果在顶层导入模块,此时它的作用域是全局的;如果在函数内部导入了模块,那它的作用域只是局部的,不能被其它函数使用。如果其它函数也要用到这个模块,还需要再次导入比较麻烦。
在用import语句导入模块时最好按照这样的顺序:
1、python 标准库模块2、python 第三方模块
3、自定义模块
这只是一些高级程序员习惯用的操作方法,在www.iplaypy.com玩蛇网python学习过程中我们尽量去学习那些大家推荐的好用的方法。
from-import语句作用
python from import语句也是导入模块的一种方法,更确切的说是导入指定的模块内的指定函数方法。
from-import语句语法
from module import name
关键字 模块名 关键字 方法名
例如入导函数math模块中的floor函数方法:
from math import floor
from math import floor #导入math模块中的floor函数方法
floor() #调用floor()函数方法