在这一篇文章里面,我打算讨论一下关于python中模块相关的一些内容。

参考了的文章

首先要提醒:模块module和包package是两个概念,简单来说一个python文件都可以说是一个module,包package本质上来讲是一种用来管理模块命名空间的方法,一个目录如果定义了一个__init__.py文件,那么这个目录就成为了一个包,通过包构建了模块在命名上的层级结构,使得模块在命名上更加自由方便了。

sys.path

在我们讨论导入module的方法之前,我们首先讨论一下程序是如何找到我们要导入的module的,实际上导入的module都需要从sys.path这个list变量中指明的路径中去寻找模块。那么sys.path都包括了哪些内容呢?

(1)运行程序所在的目录

请注意,不是当前目录,如果我运行的是python /home/yuki/test.py,那么sys.path中包含的就是test.py所在的目录/home/yuki,与我现在在哪一个目录没有关系(先别急,后面会讲到一些特殊的情况)

(2)PYTHONPATH

这个不用多说,看个例子就知道是什么意思:

/home/obj_search/anaconda3/envs/yjw/lib/python37.zip

/home/obj_search/anaconda3/envs/yjw/lib/python3.7

/home/obj_search/anaconda3/envs/yjw/lib/python3.7/lib-dynload

/home/obj_search/anaconda3/envs/yjw/lib/python3.7/site-packages

(3)取决于安装的默认设置,也就是自己加入的一些永久的路径

模块的基本知识

模块名可以通过全局变量__name__来获得,一般来说,模块名就是其文件名去掉".py"。

模块中的可执行代码会在模块第一次导入的时候执行,实际上也只有这一次。

每一个模块都有一个与之对应的符号表,可以使用dir()来查看当前模块的符号表,所谓符号表就是指在这个模块内部定义的符号。所谓符号,首先要包括一定会有的以_开头的一系列符号,如:['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']。符号还包括在模块内定义的变量、函数、类的名字。

如果我们导入了一个模块,实际上就是将这个模块的名字加入了主模块的符号表中。如果我们导入了一个模块中的一些变量、函数、类,则只是将这些类、函数、类的名字加入了主模块的符号表中,这个被导入模块的名字没有加入符号表。

模块也可以被当作一个脚本来执行,如果我们在终端运行了一个python脚本test.py,实际上也就是运行了一个名字是test的脚本,这个时候其对应的__name__全局变量的值不再是'test'而是'__main__',所以我们也经常见到if __name__ == '__main__':这样的用法。

模块的导入

简单来说就是使用import关键字,下面是示例:

import package # 将package加入符号表

from package1 import module # 将module加入符号表

from package1.package2.module import item # 将item加入符号表

import package1.package2.module # ***只将package1加入符号表

import package1.package2.module.item # ***只将package1加入符号表

import package1.package2.module as ppm # ***将ppm加入符号表

在包的__init__.py中定义__all__,可以在对包进行from package import *的时候指定导入那些module,如__all__ = ['module1', 'module2', 'module3']。 注意,__all__只在使用import *的时候才有效,如果仅仅只是import package的话,依然是按照默认导入。

如果没有定义__all__,则from package import *的时候,只能看到__init__.py的符号表,也就是说如果__init__.py中导入了某一个模块,那么导入这个包的时候也会一起导入,在__init__.py中定义的一些名字也会一起导入,但是并不是这个包里面的所有的模块都会默认导入。

所谓默认导入,也就是执行__init__.py ,在这个文件中定义的符号以及导入的符号会默认导入,其余的在这个包中的其他模块定义的符号只要没有在__init__.py导入,就并不会全部导入。

相对导入

所谓相对导入就是指使用了相对路径的导入,如下:

from . import module1 # 从当前包导入模块module1

from .. import module2 # 从当前包的父包导入模块module2

from ..package2 import module3 # 从当前包的父包的子包package2(也就是当前包的兄弟包)导入模块module3

相对导入基于模块的名字即__name__属性,所以对于主模块不能使用相对导入,因为它的该属性不是其真正的名字,而是'__main__'。

模块的__name__ 属性

接下来我们要讨论不同模块的__name__属性:

直接运行python文件(或者使用-m参数),则这个文件(模块)的__name__ = '__main__' (这种情况本质上就是将模块作为脚本执行);

该模块被另外一个模块调用,则__name__ = 该模块名 (所谓“该模块名”,实际上是根据调用关系或者说嵌套关系得到的,一般都遵从“祖父包.父包.模块名”这样的结构,注意__init__.py的__name__不包含模块名,层级结构之延伸到其所在包,其余的模块都要精确到其模块名)。

python -m参数以及两种启动方式的比较

对于一个python文件有两种启动方式(假设test.py就在当前目录底下):直接运行方式:python test.py

run library module as a script:python -m test

首先,因为两种方式都相当于执行了一个脚本文件,所以都有__name__ = '__main__';

其次,两种方式中,这个test模块对应的sys.path是不同的 ,具体来说直接运行方式中,sys.path中除了PYTHONPATH以外,还有test.py所在的目录,而使用-m参数的运行方式中,sys.path中存储的不是test.py所在的目录,而是当前目录。

虽然在上面的例子中,sys.path碰巧是相同的,我们下面举个不同的例子:

# /new/test.py

print(sys.path[0])

# python new/test.py

/root/new

# python -m new.test

/root

我们再举一个例子,来更加深入来说明sys.path不同到底带来了什么影响(当前目录为package):

package

|- subpackage1

|- __init__.py

|- a.py

|- subpackage2

|- __init__.py

|- b.py

---------------------------------

# b.py

from subpackage1 import a

---------------------------------

> python subpackage2/b.py

No module named subpackage1

---------------------------------

> python -m subpackage2.b

# 成功

关于上面两种不同的结果,由于subpackage1是在package路径下的,所以只有当sys.path中有package路径,才可以导入subpackage,显然上面只有第二种运行方式符合条件。

关于-m参数,还有一种用法:python package :执行package中的__main__.py

python -m package :先导入package(也就是执行package中的__init__.py),然后执行package中的__main__.py

实践结论

关于module和import的内容,实际还是自己实操之后更加容易理解,下面我将列举一些实践的示例,深入探讨import的种种情况。多级package嵌套的情况

.

├── b.py

└── pac

├── a.py

├── __init__.py

└── pac2

├── __init__.py

└── pac3

└── __init__.py

------------------------------------

# b.py

import pac.pac2.pac3

# 所有的__init__.py都是一样的

# __init__.py

print(__name__)

------------------------------------

> python b.py

pac

pac.pac2

pac.pac2.pac3

根据输出结果我们可以知道import pac.pac2.pac3相当于import pac \ import pac.pac2 \ import pac.pac2.pac3,而且我们还可以获得一个信息:导入package中的符号,都是要执行这个package对应的__init__.py,而不仅仅只是在导入这个package的时候才会执行__init__.py,比如说如果我们在b.py中的导入语句写的是import pac.a,实际上也需要先导入pac,也就是说要执行pac对应的__init__.py。

我们也可以在b.py中打印dir(), dir(pac), dir(pac.pac2)结果如下:

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'pac']

['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'pac2']

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'pac3']

重点观察每一个list最后一个符号, 有没有一下子弄明白了,python中如何寻找一个已经导入的符号了?对,就是层层导入,层层解析。这里抛砖引玉的说一句,上面打印的三个__name__仔细想一想你会发现他们其实都可以通过sys.path索引到。相对导入深挖

.

├── b.py

└── pac

├── a.py

└── __init__.py

------------------------------------

# b.py

from pac import a

print(dir())

# __init__.py

print(__name__)

------------------------------------

> python b.py

pac

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a']

首先b.py使用的导入方法并不是相对导入,相对导入一定要使用.和..,但是如果将b.py改为from .pac import a,又会报错。先来说一下为什么不用相对导入不会报错,因为很显然根据sys.path的路径是可以找到pac,那么 from pac import a这种写法就是合理的。

不过,相对导入的方法就不行,难道sys.path就找不到.pac吗 ?实际上,pyhton中的相对导入规定对.和..解析都是使用__name__解析的,由于我们是按照脚本方式执行的b.py,所以__name__='__main__',显然用 '__main__'当然无法通过sys.path找到对应的符号。这实际上也就是我们规定入口程序不能使用相对导入的原因,而如果不是入口程序的话,由于__name__存储的是可以通过sys.path找到对应符号的内容(前面有详细讨论过),所以使用相对导入就是可行的了。

还有一个细节值得注意,就是通过from-import方式导入的模块a,实际对应的符号是a,不是pac。

有关import的PEP

下面列举一些可能有帮助的PEP,可以根据自己的需要阅读:

参考

python中home定义是什么_关于python中的module你需要了解的相关推荐

  1. python中类的定义和使用_在Python中定义和使用类

    我正在学习Python,我有一些代码没有完成我认为它应该做的事情.我使用的是v3.4.3.最有可能的问题是:class Router: '''A representation of a router' ...

  2. python基础语法加爬虫精进_从Python安装到语法基础,这才是初学者都能懂的爬虫教程...

    Python和PyCharm的安装:学会Python和PyCharm的安装方法 变量和字符串:学会使用变量和字符串的基本用法 函数与控制语句:学会Python循环.判断语句.循环语句和函数的使用 Py ...

  3. python中home定义是什么_第48p,什么是函数?,Python中函数的定义

    原标题:第48p,什么是函数?,Python中函数的定义 大家好,我是 杨数 Tos ,这是<从 零 基础到大神>系列课程的第 48 篇文章 ,第三阶段的课程 : Python进阶知识:详 ...

  4. python中numpy数组的合并_基于Python中numpy数组的合并实例讲解

    基于Python中numpy数组的合并实例讲解 Python中numpy数组的合并有很多方法,如 - np.append() - np.concatenate() - np.stack() - np. ...

  5. python搜索pdf内容所在页码_利用Python在pdf文档中寻找某些词出现的页码

    要研究pdf文件的页码,首先要考虑这个文件的种类.pdf可能是一本书的电子版,可能是一份简历.可能是由Word.PPT或其他文档导出的--如果不是一本书,通常页面内容里是没有页码的:如果是一本书,虽然 ...

  6. python怎么理解函数的参数_理解Python中函数的参数

    定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了.对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解 ...

  7. python什么是高阶函数_说说 Python 中的高阶函数

    高阶函数(higher-order function)指的是:接受一个函数为参数,或者把函数作为结果值返回的函数1. 1 sorted() 比较常见的高阶函数是 sorted(),其内部的关键字参数 ...

  8. python中matrix是什么意思_初识Python

    初识Python 跟学习所有的编程语言一样,首先得了解这门语言的编程风格和最基础的语法.下面就让我们一起来了解一下Python的编程风格. 1.逻辑行与物理行 在Python中有逻辑行和物理行这个概念 ...

  9. python一切皆对象的理解_在 Python 中万物皆对象

    在 Python 中一切都是对象,并且几乎一切都有属性和方法.所有的函数都有一个内置的 __doc__ 属性,它会返回在函数源代码中定义的 doc string:sys 模块是一个对象,它有一个叫作 ...

  10. python中的静态方法如何调用_关于Python中如何使用静态、类、抽象方法的权威指南(译)...

    对于Python中静态.类.抽象方法的使用,我是一直很迷糊的.最近看到一篇技术文章对这方面解释的很好,在此翻译一下,加深印象,也为有需要的同学提供一个方便. Python中方法是如何工作的: 方法即函 ...

最新文章

  1. 【剑指offer】14、剪绳子
  2. 初学 Delphi 嵌入汇编[3] - 第一个 Delphi 与汇编的例子
  3. mybatis 操作动态表+动态字段+存储过程
  4. 开发日记-20190719 关键词 读书笔记《Linux 系统管理技术手册(第二版)》DAY 12
  5. 自定义图片字段调用的问题出现{dede:img ..}
  6. SVN 版本控制的数据合并规则
  7. IOS 开发 UIProgress 和 UISlidre 进度条和滑动条组件
  8. python3ide手机端怎么样_各大Python IDE的优缺点,看看哪种最适合你?
  9. C++:类占用的字节内存
  10. cms核心功能_如何根据这些重要功能选择合适的CMS
  11. 还在维护吗_你的模具生锈了吗?来了解一下这些防锈维护事项
  12. python的else_Python3 if...elseif...else语句
  13. python 基本数据结构 ndarray
  14. 高光谱地物识别练习-从ENVI标准波普库中选择端元进行物质识别
  15. python立方尾不变代码_对于这个蓝桥杯立方尾不变题我用java程序做的,正确结果应该是36,为什么我这样写结果就是12,如...
  16. 华为音量键只能调通话_华为手机音量键的隐藏功能,知道一个就会好用不少!...
  17. 大智慧705服务器文件夹,大智慧2文件目录结构.doc
  18. 藏宝阁游戏服务器维护中,梦幻西游藏宝阁异常交易保护 | 手游网游页游攻略大全...
  19. cef调用本地html,在CefSharp中使用本地构建的网页(Working with locally built web page in CefSharp)...
  20. 【漏洞通告】CVE-2022-36803 Atlassian Jira Align权限提升漏洞

热门文章

  1. TP5.1中的验证类 validate用法
  2. Python mysql-表的创建,删除和更新
  3. 【opencv学习笔记】SetImageROI函数设置ROI区域的作用及用法
  4. uva 10825 - Anagram and Multiplication(暴力)
  5. 【tool】构造朴实的测试用例
  6. 用Ruby读取Excel文件
  7. String是java中的基本数据类型吗
  8. 微软软件开发技术二十年回顾(MFC篇)
  9. 【Prison Break】第三天(3.29)
  10. 【图像处理】【去模糊】图像去模糊的原理