python 是通过module组织代码的,每一个module就是一个python文件,但是modules是通过package来组织的。
如果我们自己写着玩,有的时候就是一两个Python文件在同级目录下,但是当我们开始尝试开发更为复杂的项目的时候,package这个概念的使用就有助于我们组织我们写的一个个modules。
module的概念相对简单,所以不会再多说,主要是说一下package。
Python package
package的定义很简单,在当面目录下有__init__.py
文件的目录即为一个package。
但是这会分为两种情况,第一种情况是一个空的__init__.py
文件,另外一个情况是写了代码的__init__.py
文件。不管是空的还是有内容的,这个目录都会被认为是一个package,这是一个标识。
package的初始化工作
一个package 被导入,不管在什么时候__init__.py
的代码都只会被执行一次
>>> import package
hello world
>>> import package
>>> import package
由于 package 被导入时 __init__.py
中的可执行代码会被执行,所以小心在 package 中放置你的代码,尽可能消除它们产生的副作用,比如把代码尽可能的进行封装成函数或类。
__init__.py
内的导入顺序
当我尝试导入
from package import something
import
语句会首先检查something
是不是__init__.py
的变量,然后检查是不是subpackage
,再检查是不是module
,最后抛出ImportError
所以检查顺序如下:
-
__init__.py
文件内变量 - 是不是package内的
subpackage
- 是不是package内的
module
看个例子
我们有一个如下结构的package
在a.py
文件内有一个函数
def bar():
print("Hello, function 'bar' from module 'a' calling")
在b.py
文件内有一个函数
def foo():
print("Hello, function 'foo' from module 'b' calling")
然后我们添加一个空的__init__.py
文件在simple_package
里面。
我们看下,当我们import simple_package
的时候到底会发生什么事情(在simple_package
内激活Python shell
或者simple_package
的的路径被包含在python
的sys.path
或者在PYTHONPATH
的环境变量中)
>>> import simple_package
>>>
>>> simple_package
<module 'simple_package' from '/home/bernd/Dropbox (Bodenseo)/websites/python-course.eu/examples/simple_package/__init__.py'>
>>>
>>> simple_package.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>>
>>> simple_package.b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
我们可以看到simple_package
已经被成功导入,但是a.py
和b.py
并没有被导入
当然了,如果你希望使用import simple_package
后自动加载a
或者b
模块,这里有两种办法。
第一种就是在__init__.py
内导入a
或者b
模块,然后保存再激活python
的交互环境
#__init__.py
import a
import b
当你再次尝试import simple_package
后,就可以使用simple_package.a.bar()
来使用模块a
中的bar()
函数了。
第二办法就是手动导入,当你想使用模块a
中的bar()
函数时,需要手动导入
import simple_package.a as a
然后就是可以a.bar()
来使用bar()
函数了。
一个更复杂的例子
这是一个来自官方的例子
文件结构如下
sound
|-- effects
| |-- echo.py
| |-- __init__.py
| |-- reverse.py
| `-- surround.py
|-- filters
| |-- equalizer.py
| |-- __init__.py
| |-- karaoke.py
| `-- vocoder.py
|-- formats
| |-- aiffread.py
| |-- aiffwrite.py
| |-- auread.py
| |-- auwrite.py
| |-- __init__.py
| |-- wavread.py
| `-- wavwrite.py
`-- __init__.py
你可以将这个package的例子下载下来。如果直接使用import sound
来导入这个package,我们可以导入package sound
,但是sound
的子package(effects
,filters
,formats
)并不会被自动导入。子package不会被自动导入的原因是因为在sound
目录下的__init__.py
文件并没有任何关于导入子package的代码。
我们来看下在sound
目录下的__init__.py
的代码
"""An empty sound package
This is the sound package, providing hardly anything!"""
print("sound package is getting imported!")
然后我们导入sound
试下
>>> import sound
sound package is getting imported!
>>> sound
<module 'sound' from '/home/bernd/packages/sound/__init__.py'>
>>> sound.effects
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'sound' has no attribute 'effects'
如果你想使用子package的内容,但是在父package的__init__.py
的文件内并没有导入,你需要手动导入
>>> import sound.effects
effects package is getting imported!
>>> sound.effects
<module 'sound.effects' from '/home/bernd/packages/sound/effects/__init__.py'>
如果你希望python
帮你自动导入sound.effects
你可以往sound
目录下的__init__.py
文件写入
"""An empty sound package
This is the sound package, providing hardly anything!"""
import sound.effects
print("sound package is getting imported!")
那么你下次运行的时候python
就会自动帮你导入sound.effects
>>> import sound
effects package is getting imported!
sound package is getting imported!
当然了,除了使用绝对路径你可以使用相对路径来导入sound.effects
"""An empty sound package
This is the sound package, providing hardly anything!"""
from . import effects
print("sound package is getting imported!")
这跟linux
的命令行比较像,.
代表当前目录,..
代表上级目录
所以你可以在sound.effects
的__init__.py
文件内写入
from .. import formats
来导入sound.formats
。
当你使用sound
的时候就会发现,sound.effects
和sound.formats
都被导入了
>>> import sound
formats package is getting imported!
effects package is getting imported!
sound package is getting imported!
最后我想给你展示下,怎么从sound.effects
导入sound.filters.karaoke
模块,将一下代码加入到sound.effects
的__init__.py
文件中
"""An empty effects package
This is the effects package, providing hardly anything!"""
from .. import formats
from ..filters import karaoke
print("effects package is getting imported!")
激活python
的交互环境以后,尝试import sound
>>> import sound
formats package is getting imported!
filters package is getting imported!
Module karaoke.py has been loaded!
effects package is getting imported!
sound package is getting imported!
现在我们可以使用karaoke
的函数了
>>> sound.filters.karaoke.func1()
Funktion func1 has been called!
>>>
把你的整个package都导入进来
还是用前面的例子,这一次,我会额外的加入一个叫做foobar
的模块在主目录,你可以在这里下载例子
我们尝试使用*
来进行全部的导入
>>> from sound import *
sound package is getting imported!
我们可以看到仅仅是导入了sound
这个package但是其他的内容并没有导入。
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
__all__
我们可以使用__all__
这个魔法变量来手动导入模块和子package,当你定义了__all__
到__init__.py
文件以后,python
会根据你在list内给出的元素进行逐个导入
__all__ = ["formats", "filters", "effects", "foobar"]
所以我们可以再次导入试试
>>> from sound import *
sound package is getting imported!
formats package is getting imported!
filters package is getting imported!
effects package is getting imported!
The module foobar is getting imported
看下dir()
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'effects', 'filters', 'foobar', 'formats']
>>>
你会发现所有模块都已经被顺利导入。
那如果我们仅仅导入sound.effects
package内所有内容呢,会发生什么,我们import
的时候到底import
的是什么。
我们看下结果
>>> from sound.effects import *
sound package is getting imported!
effects package is getting imported!
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>>
你会发现他仅仅是导入了sound.effects
这个package,跟你没有修改sound
的__init__.py
之前是类似情况,仅仅是导入了这个package。
所以你也可以修改sound.effects
的__init__.py
文件来导入effects
内的所有模块
__all__ = ["echo", "surround", "reverse"]
看下结果
>>> from sound.effects import *
sound package is getting imported!
effects package is getting imported!
Module echo.py has been loaded!
Module surround.py has been loaded!
Module reverse.py has been loaded!
>>>
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'echo', 'reverse', 'surround']
>>>
总结
- 在
from package import *
语句中,如果__init__.py
中定义了__all__
魔法变量,那么在__all__
内的所有元素都会被作为模块自动被导入(ImportError
任然会出现,如果自动导入的模块不存在的话)。 - 如果
__init__.py
中没有__all__
变量,导出将按照以下规则执行:- 此 package 被导入,并且执行
__init__.py
中可被执行的代码 -
__init__.py
中定义的 variable 被导入 -
__init__.py
中被显式导入的 module 被导入
- 此 package 被导入,并且执行
网友评论