模块&包---import时发生的那些事

大型Python程序以模块module和包package的形式组织。

模块:就是一些.py文件,可以包含函数、变量、类等符号;

包:由模块及子包组成


使用import加载的模块实际上可分为4个通用类别:

  • 使用Python编写的代码(.py文件)
  • 已被编译为共享库或DLL的C或C++扩展
  • 包含一组模块的包
  • 使用C编写并链接到Python解释器的内置模块

模块module


  • import module (包括import module as xxx)
首次使用import加载模块时,它将做3件事:
  1. 创建新的命名空间,用作在相应源文件中定义的所有对象的容器。在模块重定义的函数和方法在使用global语句时将访问该命名空间。
  2. 在新创建的命名空间中执行模块中包含的代码。
  3. 在调用函数中创建名称来引用模块命名空间。这个名称与模块的名称相匹配。

e.g. 在某模块A中import math时,会在A中创建一个命名空间,并在这个命名空间中执行math.py当中的代码,并且在A中创建了math这个名称来引用这个命名空间。所以你就可以在A中用math.sqrt()和math.pi这些函数及变量了。


  • from module import symbol (包括from module import *, from module import symbol as xxx )

从模块导入选定符号

from语句用于将模块中的具体定义加载到当前命名空间中。from语句相当于import,但它不会创建一个名称来引用新创建的模块命名空间,而是将对模块中定义的一个或多个对象的引用放到当前命名空间中

e.g. 在模块A中 from math import sqrt,那么sqrt函数会直接导入到当前的命名空间中来,并没有创建新的命名空间 。所以就可以在A直接使用sqrt()函数了。


注意要点:

  • 星号(*)通配符用于加载模块中的所有定义,但以下划线开头的定义除外;
  • 通过定义列表__all__,模块可以精确控制from module import *导入的名称集合;<下图为验证>
  • 函数的全局命名空间始终是定义该函数的模块,而不是将函数导入并调用该函数的命名空间;<具体请参考Python参考手册8.2节,因与此文无关,暂且不表>
定义__all__对from module import *的影响


总结:一个模块里可以定义函数、变量、类等符号,模块里面还可以导入其他模块、导入其他模块里面的符号。模块还可以定义一个__all__列表,用以控制from module import * 这条语句的效果。



包package

包可用于将一组模块分组到一个常见的包名称下。这项技术有助于解决不同应用程序中使用的模块名称之间的命名空间冲突问题。包是通过使用与其相同的名称创建目录,并在该目录中创建文件__init__.py来创建的。如果需要,可以向该目录中放入其他源文件、编译后的扩展和子包。

例如下面:包package包含两个子包pack1、pack2,还包含一个func模块

包的常见结构


QUESTION:要使用包里面的函数,难道只能package.func.xxx或者package.pack1.module_11.xxx这样调用吗?可是很多时候我们导入一个包,可以直接用一个函数的。比如import requests,就可以直接用requests.get()函数了。这是怎么发生的呢?


  • 只要第一次导入包中的任何部分,就会执行文件__init__.py中的代码。

<上面模块的导入讲到,第一次导入一个模块也会执行这个模块>,所义,其实导入一个包,就是导入这个包中的__init__.py模块,但是用包名来引用这个模块。


  • from package import * 要小心

from module import * 会将模块中的符号都导入(如果定义了__all__的话,就导入__all__列表定义的内容)。

本来希望from package import *将与某个包相关联的所有子模块导入到当前命名空间中。但是由于各个系统之间的文件名约定不同,Python无法准确的确定各个模块的具体内容。结果,该语句只会导入package目录的__init__.py文件中定义的所有名称(包括__init__.py文件定义的函数,导入的内容等)。当然__init__.py也是个模块,所以这种行为可以通过定义__init__.py文件的列表__all__来修改。


查看不同包的__all__


  • 单独导入包名(import package)不会导入包中所包含的所有子模块。

这句话要一分为二地看:

  • 当包的__init__.py文件为空时,导入包名没法使用包内的子包及模块
__init__.py为空,from pack1 import *无法导入子模块


  • 包的__init__.py并不为空

我们说,导入一个包,会执行包的__init__.py文件,那么就可以在这上面做文章了。

想一想,我们requests库时,不是直接import requests,然后就可以直接使用requests.get()等方法了吗?

requets库的__init__.py文件


在requests库的__init__.py文件中,已经把一些常用的方法导进来了。


总结:

  • 第一次导入一个模块,会执行这个模块
  • 可以通过修改模块module的__all__列表,来改变from module import * 时的效果
  • 导入一个包,其实就是导入包的__init__.py模块
  • 如果包的__init__.py模块为空,那么import package这样的语句是不能使用包当中的任何模块的
  • 如果包的__init__.py模块为空,那我们只能使用import package.module或者from package import module这样的导入方式
  • __init__.py也是个模块,其实也可以在__init__.py中直接定义函数fun,那样import package就可以直接用package.fun这个函数了是吧。但是我们一般不会这么干,这样会使__init__.py文件太乱
  • __init__.py也是个模块,那也可以在这个模块中导入其他模块,这样import package时,就能直接使用一些符号了。
  • __init__.py也是个模块,也可以定义__all__列表变量,控制from package import * 的作用。


综上

  • 导入一个包,其实就是导入包的__init__.py模块。
  • 这个模块里面可定义的内容跟一般的模块没什么两样。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值