查看原文
其他

Python模块包导入

点击上方 "Python人工智能技术关注,星标或者置顶
22点24分准时推送,第一时间送达
后台回复“大礼包”,送你特别福利

编辑:乐乐 | 来自:Python编程学习圈

Pythn人工智能技术(ID:coder_experience)第713期推文

上一篇:Python3.10几个牛批新特性!


正文


大家好,我是Python人工智能技术

从下面的目录结构可以看出,mypackage是一个包,其中包含了ab两个子模块且其中包含各一个文件,而且每个模块都包含了__init__.py这个文件。

$ treemypackage|____ __init__.py|____ a| |____ __init__.py| |____ bar.py|____ b |____ __init__.py |____ foo.py

$ cat mypackage/a/bar.pyBAR = 1
$ cat mypackage/b/foo.pyFOO = 2

如下所示,就是最基础的导入方法,也叫做隐式的相对导入。隐式的相对导入就是没有告诉解释器我这个import相对于谁导入的,但是它默认的是相对的是当前的模块。

In [1]: import mypackage
In [2]: import mypackage.a
In [3]: import mypackage.a.bar
In [4]: from mypackage import a
In [5]: from mypackage.b import foo
In [6]: from mypackage.a.bar import BAR
  • 显式导入和隐式导入

Python包导入的这个概念里面,并没有一个绝对的导入,因为它没有Linux系统的根文件路径这个概念。在Python中,都是通过相对导入的,只不过分为显式导入和隐式导入。

现在,我们修改bar.py中的内容如下所示,之后我们引用bar.py的时候就能够使用foo.py中的FOO变量了。其中,.代表了当前模块,而..代表了上层的模块,...就代表了再上层模块,以此类推。

$ cat mypackage/a/bar.pyBAR = 1from ..b.foo import FOO
In [1]: from mypackage/a/bar.py import FOO
In [2]: FOOOut[2]: 2

当然,我们还可以使用如下两种的相对导入的写法。第一种使用了.就是相对于当前模块导入的foo这个模块,而第二种使用了.foo就是相对于当前模块的foo模块导入了FOO这个变量。其中,导入的方式不同,调用的方式也就不同了。

我们可以看到,在__init__.py中导入包下的某些模块或者模块里面的内容,这种方式是比较流行的。它相当于给这个包,提供了一个入口,我不再需要在这个模块里面的子模块去找了。

$ cat mypackage/b/__init__.pyfrom . import foofrom .foo import FOO
In [1]: from mypackage/b import foo, FOO
In [2]: foo.FOO, FOOOut[2]: (2, 2)
  • 相对导入适合场景

第一个就是,在大型项目中,代码目录非常复杂,模块层级很深。使用相对导入可维护性更强,导入语句更简洁。第二个就是,在项目早期,由于需求变动很大,会偶尔改变某一个顶层包的名字或者移动位置。

一般情况下,如果模块的层级超过三级就需要使用这种显式的相对导入方法,否则就会使用默认的那种隐式的相对导入。

可以看下Flask里面的入口文件__init__.py里面的内容,分为三个部分。第一部分是非Flask包里面的内容,第二部分是Flask包内模块中的类和函数,第三部分直接导入json这个子包,而且因为非常常用而暴露了jsonify这个json函数。

$ cat flask/__init__.pyfrom werkzeug.exceptions import abortfrom werkzeug.utils import redirectfrom jinja2 import Markup, escape
from .app import Flask, Request, Responsefrom .config import Configfrom .blueprints import Blueprintfrom .templating import render_template, render_template_string...
from . import jsonjsonify = json.jsonify
  • 常见模块导入错误

当我们在Python2中使用IPython或者Python解释器的时候,相对导入只能放在代码里面,而不能直接运行的。而在Python3中相对导入会有一些区别,相对于当前目录是可以导入,其他的都会报错。那什么方法是对的呢?就是之前的哪个Flask入口文件的示例,将导入写在代码里面就可以了。另外搜索公众号Java架构师技术后台回复“面试题”,获取一份惊喜礼包。

# Python2In [1]: from . import mypackage---------------------------------------------------------------------------ValueError Traceback (most recent call last)<ipython-input-1-b2a156b38b60> in <module>()----> 1 from . import mypackage
ValueError: Attempted relative import in non-package
In [2]: from .mypackage import b---------------------------------------------------------------------------ValueError Traceback (most recent call last)<ipython-input-2-0086e0c5eb87> in <module>()----> 1 from .mypackage import b
ValueError: Attempted relative import in non-package
# Python3In [1]: from . import mypackage
In [2]: mypackageOut[2]: <module 'mypackage' from '/Users/escape/mypackage/__init__.py'>
In [3]: from .mypackage import b---------------------------------------------------------------------------ModuleNotFoundError Traceback (most recent call last)<ipython-input-18-0086e0c5eb87> in <module>()----> 1 from .mypackage import b
ModuleNotFoundError: No module named '__main__.mypackage'; '__main__' is not a package
In [4]: from .mypackage.b import FOO---------------------------------------------------------------------------ModuleNotFoundError Traceback (most recent call last)<ipython-input-19-9efb2f0c110b> in <module>()----> 1 from .mypackage.b import FOO
ModuleNotFoundError: No module named '__main__.mypackage'; '__main__' is not a package
  • 禁用隐式的相对导入

这个小节的语句from __future__ import absolute_import,可能之前我们都见过,它会禁用隐式的相对导入(implicit relative import)而不会禁用显式的相对导入(explicit relative import)。

# 隐式相对导入In [1]: import mypackage
# 显式相对导入In [2]: from . import mypackage

像上面的示例中,是可以导入是可以运行的,以为mypackage这个名称一般都不会有冲突的。但是,如果和标准库有冲突的话,那就有问题了。如果我们想引用标准库中的os模块,却引入了mypackage模块下的os文件中的内容,那不就尴尬了。

$ cat mypackage/os.pya = 1
$ cat mypackage/__init__.pyfrom os import a

# Python2In [1]: from mypackage import a
In [2]: aOut: 1

而当我们禁用隐式的相对引入,再次执行的时候,会报错提示说os模块中并没有a这个模块。因为它引用的是标准库中的os模块,并不是我们定义的。

而在Python3中,是禁用隐式的相对导入这种方式的。不是不允许我们这样使用,而是导入os模块的话会优先引入标准库模块。

$ cat mypackage/__init__.pyfrom __future__ import absolute_importfrom os import a
In [1]: from mypackage import a---------------------------------------------------------------------------ImportError Traceback (most recent call last)<ipython-input-1-aba2f1bd100d> in <module>()----> 1 from mypackage import a/Users/escape/mypackage/__init__.py in <module>() 1 from __future__ import absolute_import----> 2 from os import a
ImportError: cannot import name a
  • 优先属性和模块的导入

我们知道,使用from xxx import *这样一次性导入xxx模块下全部内容的方式的写法是一个不好的形式。所以,可以在包的__init__.py文件里面,加入一个__all__这样一个属性。不在__all__之后的列表中,是不能通过import *的这种方式导入的。

这样,通常是为了减少模块导入的时间,也减少了不必导入的内容。但是这样使用方式,只适用于import *的这种方式。所以,使用from mymodule import b也是可以导入b的。

$ cat mymodule.py__all__ = ['a']a = 1b = 2
In [1]: from mymodule import *
In [2]: aOut[2]: 1
In [3]: b---------------------------------------------------------------------------NameError Traceback (most recent call last)<ipython-input-3-89e6c98d9288> in <module>()----> 1b
NameError: name 'b' is not defined


你还有什么想要补充的吗?

免责声明:本文内容来源于网络,文章版权归原作者所有,意在传播相关技术知识&行业趋势,供大家学习交流,若涉及作品版权问题,请联系删除或授权事宜。


技术君个人微信


添加技术君个人微信即送一份惊喜大礼包


→ 技术资料共享

→ 技术交流社群



--END--


往日热文:

看看人家那物业管理系统,那叫一个优雅(附源码)

一款神仙接私活儿软件,吊到不行!

保姆级别!带你搭建一台服务器!

用Python爬取高颜值美女(爬虫+人脸检测+颜值检测)

2021编程语言排行榜出炉!

Python 强大的信号库 blinker 入门教程

17个常见的Python运行时错误,你中招了没?

警告!VPN翻墙被抓了!已大规模行政处罚!


Python程序员深度学习的“四大名著”:



这四本书着实很不错!我们都知道现在机器学习、深度学习的资料太多了,面对海量资源,往往陷入到“无从下手”的困惑出境。而且并非所有的书籍都是优质资源,浪费大量的时间是得不偿失的。给大家推荐这几本好书并做简单介绍。


获得方式:

1.扫码关注本公众号
2.后台回复关键词:名著

▲长按扫描关注,回复名著即可获取

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存