包导入格式

导入模块时除了使用模块名进行导入,还可以使用目录名进行导入。例如,在sys.path路径下,有一个dir1/dir2/mod.py模块,那么在任意位置处都可以使用下面这种方式导入这个模块。

import dir1.dir2.mod
from dir1.dir2.mod import XXX

一个实际一点的示例,设置PYTHONPATH环境变量为d:\pypath,然后在此目录下创建以上目录和mod.py文件:

set PYTHONPATH="D:\pypath"
mkdir d:\pypath\dir1\dir2
echo print("mod.py") >d:\pypath\dir1\dir2\mod.py
echo x=3 >>d:\pypath\dir1\dir2\mod.py # 进入交互式python
>>> import dir1.dir2.mod
mod.py
>>> dir1.dir2.mod.x
3

注1:在python3.3版本及更高版本是可以导入成功的,但是在python3.3之前的版本将失败,因为缺少__init__.py文件,稍后会解释该文件

注2:顶级目录dir1必须位于sys.path列出的路径搜索列表下

如果输出dir1和dir2,将会看到它们的是模块对象,且是名称空间

>>> import dir1.dir2.mod
mod.py >>> dir1
<module 'dir1' (namespace)> >>> dir1.dir2
<module 'dir1.dir2' (namespace)> >>> dir1.dir2.mod
<module 'dir1.dir2.mod' from 'd:\\pypath\\dir1\\dir2\\mod.py'>

这种模块+名称空间的形式就是包(严格地说是包的一种形式),也就是说dir1是包,dir2也是包,这种方式是包的导入形式。包主要用来组织它里面的模块。

从上面的结果也可以看出,包也是模块,所以能使用模块的地方就能使用包。例如下面的代码,可以像导入模块一样直接导入包dir2,包和模块的区别在于它们的组织形式不一样,模块可能位于包内,仅此而已。

import dir1.dir2
from dir1 import dir2

另外,导入dir1.dir2.mod时,它声明的模块变量名为dir1,而不是dir1.dir2.mod,但是导入的对象却包含了3个模块:dir1、dir1.dir2以及dir1.dir2.mod。如下:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'dir1'] >>> for key in sys.modules:
... if key.startswith("dir1"):
... print(key,":",sys.modules[key])
...
dir1 : <module 'dir1' (namespace)>
dir1.dir2 : <module 'dir1.dir2' (namespace)>
dir1.dir2.mod : <module 'dir1.dir2.mod' from 'd:\\pypath\\dir1\\dir2\\mod.py'>

__init__.py文件

上面的dir1和dir1.dir2目前是空包,或者说是空模块(再一次强调,包就是模块)。但并不意味着它们对应的模块对象是空的,因为模块是对象,只要是对象就会有属性。例如,dir1包有如下属性:

>>> dir(dir1)
['__doc__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'dir2']

之所以称为空包,是因为它们现在仅提供了包的组织功能,而且它们是目录,而不像py文件一样,是实实在在的可以编写模块代码的地方。换句话说,包现在是目录文件,而不是真正的模块文件。

为了让包"真正的"成为模块,需要在每个包所代表的目录下加入一个__init__.py文件,它表示让这个目录格式的模块(也就是包)像py文件一样可以写模块代码,只不过这些模块代码是写入__init__.py中的。当然,模块文件中允许没有任何内容,所以__init__.py文件也可以是空文件,它仅表示让包成为真正的模块文件。

每次导入包的时候,如果有__init__.py文件,将会自动执行这个文件中的代码,就像模块文件一样,事实上它就是让目录代表的包变成模块的,甚至可以说它就是包所对应的模块文件(见下面示例),所以也可以认为__init__.py是包的初始化文件。在python3.3之前,这个文件必须存在,否则就会报错,因为它不认为目录是有效的模块。

现在,在dir1和dir2下分别创建空文件__init__.py

type nul>d:\pypath\dir1\__init__.py
type nul>d:\pypath\dir1\dir2\__init__.py

现在目录的层次格式如下:

λ tree /f d:\pypath
D:\PYPATH
└─dir1
│ __init__.py
└─dir2
mod.py
__init__.py

再去执行导入操作,并输出包dir1和dir2。

>>> import dir1.dir2.mod
mod.py >>> dir1
<module 'dir1' from 'd:\\pypath\\dir1\\__init__.py'> >>> dir1.dir2
<module 'dir1.dir2' from 'd:\\pypath\\dir1\\dir2\\__init__.py'> >>> dir1.dir2.mod
<module 'dir1.dir2.mod' from 'd:\\pypath\\dir1\\dir2\\mod.py'>

从输出结果中不难看出,包dir1和dir1.dir2是模块,且它们的模块文件是各自目录下的__init__.py

实际上,包分为两种:名称空间模块、普通模块。名称空间包是没有__init__.py文件的,普通包是有__init__.py文件的。无论是哪种,它都是模块。

__init__.py写什么内容

既然包是模块,而__init__.py文件是包的模块文件,这个文件中应该写入什么代码?答案是可以写入任何代码,我们只需把它当作一个模块对待就可以。不过,包既然是用来组织模块的,真正的功能性属性应该尽量写入到它所组织的模块文件中(也就是示例中的mod.py)。

但有一项__all__是应该在__init__.py文件中定义的,它是一个列表,用来控制from package import *使用*导入哪些模块文件。这里的*并非像想象中那样会导入包中的所有模块文件,而是只导出__all__列表中指定的模块文件。

例如,在dir1.dir2包下有mod1.py、mod2.py、mod3.py和mod4.py,如果在dir2/__init__.py文件中写入:

__all__ = ["mod1", "mod2", "mod3"]

则执行:

from dir1.dir2 import *

不会导入mod4,而是只导入mod1-mod3。

如果不设置__all__,则from dir1.dir2 import *不会导入该包下的任何模块,但会导入dir1和dir1.dir2。

__path__属性

严格地说,只有当某个模块设置了__path__属性时,才算是包,否则只算是模块。这是包的绝对严格定义。

__path__属性是一个路径列表(可迭代对象即可,但通常用列表),和sys.path类似,该列表中定义了该包的初始化模块文件__init__.py的路径。

只要导入的是一个包(无论是名称空间包还是普通包),首先就会设置该属性,默认导入目录时该属性会初始化当前目录,然后去该属性列出的路径下搜索__init__.py文件对包进行初始化。默认情况下由于__init__.py文件后执行,在此文件中可以继续定义或修改__path__属性,使得python会去找其它路径下的__init__.py对模块进行初始化。

以下是默认初始化后的__path__值:

>>> import dir1.dir2
>>> dir1.dir2.__path__
['d:\\pypath\\dir1\\dir2'] >>> import dir1.dir3
>>> dir1.dir3
<module 'dir1.dir3' (namespace)>
>>> dir1.dir3.__path__
_NamespacePath(['d:\\pypath\\dir1\\dir3'])

一般来说,几乎不会设置__path__属性。

导入示例

import和from导入时有多种语法可用,这两个语句的导入方式和导入普通模块的方式是一样的:import导入时需要使用前缀名称去引用,from导入时是赋值到当前程序的同名全局变量中。如果不了解,请看前一篇文章:python模块导入细节

假设现在有如下目录结构,且d:\pypath位于sys.path列表中:

$ tree -f d:\pypath
d:\pypath
└── dir1
├── __init__.py
└── dir2
├── __init__.py
└── mod.py

只导入包:

import dir1             # 导入包dir1
import dir1.dir2 # 导入包dir1.dir2
from dir1 import dir2 # 导入包dir1.dir2

导入某个模块:

import dir1.dir2.mod
from dir1.dir2 import mod

如果dir2/__init__.py中设置了__all__,则下面的导入语句会导入已设置的模块:

from dir1.dir2 import *

注意,只支持上面这种from...import *语法,不支持import *

导入模块中的属性,比如变量x:

from dir1.dir2.mod import x

相对路径导入

注:如果允许,不要使用相对路径导入,很容易出错,特别是对新手而言。使用绝对路径导入,并将包放在sys.path的某个路径下就可以。

假设现在有如下目录结构:

$ tree -f d:\pypath
d:\pypath
└── dir1
├── __init__.py
├── dir4
│   ├── __init__.py
│   ├── c2.py
│   └── c1.py
├── dir3
│   ├── __init__.py
│   ├── b3.py
│   ├── b2.py
│   └── b1.py
└── dir2
├── __init__.py
├── a4.py
├── a3.py
├── a2.py
└── a1.py

在dir1.dir2.a1模块文件中想要导入dir1.dir3.b2模块,可以在a1.py中使用下面两种方式导入:

import dir1.dir3.b2
from dir1.dir2. import b2

上面的导入方式是使用绝对路径进行导入的,只要使用绝对路径,都是从sys.path开始搜索的。例如,上面是从sys.path下搜索dir1,再依次搜索dir1.dir3.b2。

python还支持包的相对路径的导入,只要使用...即可,就像操作系统上的相对路径一样。使用相对路径导入时不会搜索sys.path。

相对路径导入方式只有from...import支持,import语句不支持,且只有使用...的才算是相对路径,否则就是绝对路径,就会从sys.path下搜索

例如,在a1.py中导入dir1.dir3.b2:

from ..dir3 import b2

注意,必须不能直接python a1.py执行这个文件,这样会报错:

    from ..dir3 import b2
ValueError: attempted relative import beyond top-level package

报错原因稍后解释。现在在交互式模式下导入,或者使用python -m dir1.dir2.a1的方式执行。

>>> import dir1.dir2.a1

以下几个示例都如此测试。

在a1.py中导入包dir3:

from .. import dir3

在a1.py中导入dir1.dir2.a2,也就是同目录下的a2.py:

from . import a2

导入模块的属性,如变量x:

from ..dir3.b2 import x
from .a2 import x

相对路径导入陷阱

前面说过一个相对路径导入时的错误:

    from ..dir3 import b2
ValueError: attempted relative import beyond top-level package

dir3明明在dir1下,在路径相对上,dir3确实是a1.py的../dir3,但执行python a1.py为什么会报错?

from ..dir3 import b2

这是因为文件系统路径并不真的代表包的相对路径,当在dir1/a1.py中使用..dir3,python并不知道包dir1的存在,因为没有将它导入,没有声明为模块变量,同样,也不知道dir2的存在,仅仅只是根据语句知道了dir3的存在。但因为使用了相对路径,不会搜索sys.path,所以它的相对路径边界只在本文件。所以,下面的导入也是错误的:

from . import a2

实际上,更标准的解释是,当py文件作为可执行程序文件执行时,它所在的模块名为__main__,即__name____main__,但它并非一个包,而是一个模块文件,对它来说没有任何相对路径可言。

解决方法是显式导入它们的父包,让python记录它的存在,只有这样才能使用..

python -m dir1.dir2.a2

还有几个常见的相对路径导入错误:

from .a3 import x

错误:

ModuleNotFoundError: No module named '__main__.a3'; '__main__' is not a package

原因是一样的,py文件作为可执行程序文件执行时,它所在的模块名为__main__,它并非一个包。

最后,建议在条件允许的情况下,使用绝对路径导入,而不是相对路径。

使用别名导入

通过包的导入方式也支持别名。例如:

from dir1.dir2.a2 import x as xx
print(xx) import dir1.dir2.a2 as a2
print(a2.x) from dir1.dir2 import a2 as a22
print(a22.x)

python包导入细节的更多相关文章

  1. 【转】python包导入细节

    [转]python包导入细节 包导入格式 导入模块时除了使用模块名进行导入,还可以使用目录名进行导入.例如,在sys.path路径下,有一个dir1/dir2/mod.py模块,那么在任意位置处都可以 ...

  2. python模块导入细节

    python模块导入细节 官方手册:https://docs.python.org/3/tutorial/modules.html 可执行文件和模块 python源代码文件按照功能可以分为两种类型: ...

  3. 【转】python模块导入细节

    [转]python模块导入细节 python模块导入细节 官方手册:https://docs.python.org/3/tutorial/modules.html 可执行文件和模块 python源代码 ...

  4. python 包导入规则

    python 包导入规则,恶心了一天,终于搞清楚了 1.目录 speed data __init__.py __init__.py static templates view __init__.py ...

  5. mxnet的python包导入的前前后后

    mxnet底层的代码是cpp写的, 然后在它上面封装提供了R, python, scala和matlab的接口. 我现在只关注python. 如何使用mxnet的python接口? 自然是通过impo ...

  6. Python 包导入

    首先我们先了解下python中寻找模块的顺序 是否是内建模块 ->主目录 ->PYTHONPATH环境变量 ->标准库 -> 首先判断这个model是否是built-in,即内 ...

  7. python包导入

    https://www.cnblogs.com/foremostxl/p/9501663.html

  8. 将python包发布到PyPI和制作whl文件

    参考链接:wheel和egg的不同怎样将自己写的包传达到PyPi发布你自己的轮子 - PyPI打包上传实践PyPI官网上传包教程 wheel文件Wheel和Egg都是python的打包格式,目的是支持 ...

  9. Python 包的相对导入讲解

    [Python 包的相对导入讲解] 参考:http://www.dai3.com/python-import.html

随机推荐

  1. Cache高速缓冲存储器

    Cache的命中率:命中Cache的次数比总访问次数 平均访问时间:t(Cache)X命中次数+t(未命中)X未命中次数 Cache与主存的映射方式: 直接映射 全相联映射 组相联映射 图片来源:ht ...

  2. Python基础-if判断以及其他循环

    if else 判断: #!/usr/bin/env python # -*- coding:utf-8 -*- import getpass name = input("用户名??&quo ...

  3. Exp1 PC平台逆向破解----20164325 王晓蕊

    前言:实验中用到的知识 JE:条件转移指令,如果相等则跳转: JNE:条件转移指令(等同于“Jump Not Equal”),如果不相等则跳转: JMP:无条件跳转指令.无条件跳转指令可转到内存中任何 ...

  4. 项目设计day1

    项目内容:一个实时监控斗鱼TV某个主播弹幕的设计 通过python爬虫获取当前弹幕,通过flume采集数据,接下来数据分为线上和线下两种方案: 线上:实时分析,分为两种方案:(1) flume+kaf ...

  5. DataOutputStream and DataInputStream

    1.在io包中,提供了两个与平台无关的数据操作流 数据输出流(DataOutputStream) 数据输入流(DataInputStream) 2.通常数据输出流会按照一定的格式将数据输出,再通过数据 ...

  6. #254 Check for Palindromes

    Check for Palindromes 检查回文字符串 如果给定的字符串是回文,返回true,反之,返回false. 如果一个字符串忽略标点符号.大小写和空格,正着读和反着读一模一样,那么这个字符 ...

  7. 全国计算机等级考试二级Python语言程序设计考试大纲

    全国计算机等级考试二级Python语言程序设计考试大纲(2018年版) 基本要求 掌握Python语言的基本语法规则. 掌握不少于2个基本的Python标准库. 掌握不少于2个Python第三方库,掌 ...

  8. Android语音识别

    语音识别 - 科大讯飞 开放平台 http://open.voicecloud.cn/ 需要拷贝lib.assets.并在清单文件中写一些权限 public class MainActivity ex ...

  9. LeetCode题解33.Search in Rotated Sorted Array

    33. Search in Rotated Sorted Array Suppose an array sorted in ascending order is rotated at some piv ...

  10. 应用监控CAT之cat-client源码阅读(一)

    CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...