Python中的模块与包1

版权声明:原创文章转载请注明出处~ https://blog.csdn.net/PecoHe/article/details/90056653

11.1 模块介绍

模块是一个Python文件,该文件包含相关的定义与语句(类,函数,变量)。模块具有名称,名称与文件的名称一致。
模块具有如下的好处:

  • 通过划分若干个模块,我们就可以将项目程序进行明确的划分,从而将复杂问题简单化,同时,也能够进行更加合理的分工,有利于协作式开发程序。
  • v模块提供独立的命名空间,可以解决命名上的冲突。不同的模块中,定义相同名称的变量,不会产生命名冲突。
  • 模块可以实现良好的重用性,在多人间实现共享。

11.2 模块的使用

大的项目,可以划分为多个模块。但模块与模块之间,不可能都是完全孤立的。一个模块很可能需要与其他的一个(或多个)模块进行交互,因此,如果我们需要在一个模块中使用其他模块定义的名称(函数,类,变量等),则需要首先导入该模块,然后通过模块名.名称进行访问。

11.2.1 导入模块

我们可以使用import对模块进行导入:

import 模块

也可以一次导入多个模块:

import 模块1,模块2……

当导入模块时,该模块内的语句就会得到执行,但只会执行一次,即重复导入模块不会多次执行。当模块作为脚本执行时(使用python命令在命令行执行),模块中的语句也会得到执行。按照惯例,模块导入语句写在模块的最上方。
当导入模块后,可以使用模块中所定义的名称,语法格式为:

模块名.名称
模块的访问

也可以将其他模块中定义的名称(全局命名空间中的名称)直接导入到当前模块的命名空间,这样就可以直接通过名称访问,而无需使用模块名限定。语法格式如下:

from 模块 import 名称1,名称2……

不过,此种导入方式要稍加留意,因为如果当前模块中也存在同样名称的定义,就会造成名称的冲突,也就是名称会重新绑定后来的对象

假设我们现在不考虑名称的冲突,只考虑访问的便捷性,使用from import的方式确实是不错的选择。但是,如果我们要使用该模块中定义的很多名称,一个个的导入可能会有些繁琐,此时,我们可以使用批量导入:

from 模块 import *

这样就会将模块中除了以_开头的所有名称导入到当前模块的命名空间。然而,这种方式会导入很多名称,容易造成命名冲突,尽可能少用。

dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。

dir([object])

11.2.2 模块别名

当导入模块(或模块中的名称)时,我们可以使用as为模块(或模块中的名称)指定别名。语法如下:

import 模块名 as 模块别名
from 模块名 import 名称 as 名称别名

这样,我们就可以通过别名来访问模块(或模块中的名称)。但是,一旦指定别名后,原模块名(或原模块中的名称)将不再可用
指定别名具有如下好处:

  • 在使用import 或 from时,如果产生命名冲突,可以使用别名来解决。
  • 如果模块或名称较长,可以使用简短的别名,减少输入量。

11.2.3 隐藏模块数据

因为使用import *的语法会在当前命名空间增加很多名称,为了减少import *所造成的影响,即名称冲突,我们可以有选择性的隐藏模块的数据,进而限制使用import *时,名称的导入。
隐藏模块数据可以采用两种方式:

  • 将名称以下划线(_)开头。
  • 定义__all__变量。

说明:
以上两种隐藏方式仅是限制使用import *语法导入的名称,并不是表示该名称无法在模块外访问。使用import其他方式导入,还是能够在模块外进行访问的。

  • 以下划线(_)开头定义名称
    如果模块中定义的名称以_开头,则在使用import *语法时,这些名称不会导入到当前模块的命名空间中,即当前模块无法直接访问该名称。
  • all
    如果使用from 模块 import *的语法,则默认情况下会导入模块中除_开头的所有名称,这容易与当前模块的命名造成冲突。我们可以定义__all__变量来限制导出名称的数量。当在模块中定义__all__变量时,该变量的值为一个字符串列表,只有列表中指定的名称才会导入到当前命名空间。
  • 两种方式的顺序
    当在__all__变量中指定了以下划线()开头的名称时会首先检查__all__变量,如果存在,会将__all__中指定的名称导入到当前的命名空间。如果不存在,则会将除下划线()开头的名称导入到当前的命名空间。由判定顺序角度可以得知:__all__中即使指定以下划线(_)开头的名称,该名称依然可以成功导入到当前的命名空间中。

11.2.4 name

在编写模块时,我们可能需要对当前模块的功能进行测试。
可是,当我们在其他模块中导入当前模块时,测试内容就会得到执行,这并不是我们想要的。因此,我们不得不将其去掉或者注释。可是,在以后修改该模块时,例如,又增加了一个新的函数,我们还需要再写入新的测试代码来验证新增功能的正确性,如此反复,势必会带来一定的不方便性。
解决之道:
我们可以通过__name__属性来获取模块的名称。之前我们提过,模块会在两种情况下执行:

  • 当模块由其他模块导入时。
  • 当模块作为脚本,在命令行使用python 文件名.py执行时。

但是,两种执行方式,通过__name__获取的模块名称是不一致的。当模块由其他模块导入时,模块的名称就是文件的名称,而模块作为脚本执行时,模块的名称为__main__。因此,我们可以据此获取模块执行的方式。

11.3 模块搜索路径

当我们导入模块时,解释器会按照如下顺序搜索:

  1. 在解释器内建模块中搜索指定的模块名。例如,sys,math等。
  2. 作为脚本运行文件所在的路径。
  3. PYTHONPATH环境变量指定的路径。我们可以根据需要设置该路径,如果没有该环境变量,则忽略。
  4. 与Python安装相关的路径。这包含Python语言的内建模块(os, random模块),以及安装的第三方模块(beautifulsoup,numpy模块)所在的路径等。

我们可以通过sys模块的path属性获得路径信息。path的值(列表类型)就是由后三项按顺序组成的(后三项路径的并集)。解释器会在这些路径中寻找模块名.py文件。
解释器会按顺序进行查找,以先找到的为准。因此,如果在不同的路径中都存在满足条件的模块,会以先找到的为准。

11.4. 模块的缓存

当我们导入模块时,Python会缓存所加载模块编译后的版本。从实现的角度,就是将导入模块的文件(.py)编程成字节码文件(.pyc)。这样在下次运行时,就会检查字节码所对应的源文件在编译过后是否经过修改,即字节码文件是否是对应源文件的最新版本。如果不是最新版本,则会对源文件重新进行编译。如果已经是最新版本,则会从字节码文件中读取信息,这样可以加快模块的加载速度。
编译的字节码(*.pyc)文件是平台无关的,即不管在什么平台,只要Python源文件的内容相同,编译过后的字节码文件也相同。字节码文件会保存在与作为脚本运行文件相同路径下的__pycache__目录中。
格式为“模块名.解释器-版本.pyc”,例如,假设导入的模块为test,解释器为CPython3.6,则字节码文件名为“test.cpython-36.pyc”。
说明:

  • 缓存字节码文件只是可以提供模块加载的速度,并不会提高模块运行的速度。
  • 只有导入的模块才会生成字节码文件,作为脚本运行的文件不会生成字节码文件。
  • 字节码文件可以脱离源文件而运行。

猜你喜欢

转载自blog.csdn.net/PecoHe/article/details/90056653