python 模块及模块包

python 中模块和模块包

模块相关概念

模块是高级别的程序组织单元,它将程序代码和数据封装起来以便重用。
模块可以通过下面两个语句导入

  1. import
  2. from

模块的作用

  1. 代码重用
  2. 系统的划分命名空间
  3. 实现共享服务和数据

python import模块如何工作

python在执行import语句时,会有下面三个操作:

1. 找到模块文件

   首先python会把载入的模块存储到一个名为sys.modules的表中,并在一次导入操作的开始检查该表。
   如果模块不存在,将会启动搜索过程(一旦导入一次此模块就存在于sys.modules了,
   当再次import 时,必须首先reload,不然修改不生效):

搜索路径顺序

  • 程序的主目录

    首先python会在运行文件的当前目录内搜索导入的文件,如果运行的是顶层文件(此时路径为顶层文件当前路径)。
    交互式窗口中运行,这一入口就是当前工作目录。这个搜索是第一优先级的。
    如果在这个目录下搜索到对应名字的脚本,将会使用此脚本。
    所以避免和库文件使用相同的名字。除非你清楚的知道这个问价你是要替换掉库文件。
    
  • PYTHONPATH 目录

      如果程序的主目录中没有找到对应的脚本文件,则执行PYTHONPATH 目录。
      PYTHONPATH 是指:电脑中的环境变量中名字为PYTHONPATH 的变量值。
      设置步骤:我的电脑--属性-->高级-->环境变量,新建/编辑 PYTHONPATH的变量,将路径添加进去。
    
  • 标准连接库目录

    当以上两个都没有的时候,python会自动搜索标准库模块安装在及机器上的那些目录。
    这些通常是不需要安装在PYTHONPATH中的
    
  • 任何.pth文件的内容

       最后,python有个新的功能,允许用户把有效的目录添加到模块搜索路径中去,
       也就是后缀名 为.pth的文本文件中一行一行的列出来。
       但是.pth必须放在python中的sitepackages中,如何判断是否是sitepackages:
    
        a) 将.pth文件放入python中定义的用户的sitepackages
    
import site
site.getsitepackages()
通过这个命令一般会得到两个路径,[0]是user的site-packages,[1]是库文件所在的site-packages.
一般我们建议把自己定义的my.pth放在user的site-packages

        b) 用户定义自己的某个folder路径为sitepackages**
import site
site.addsitedir(YOUR_PATH)
  执行完此条命令后,python会把路径下的.pth文件中的路径添加到sys.path中。
  因此此方法等于通过sys.path.append()语句一条一条的添加 

总结:无论通过哪种方法来添加搜索路径,最终的搜索路径都会出现在sys.path中。我们可以直接通过添加到sys.path中来增加可见性。其中搜索顺序是按照sys.path中的顺序进行的。

2 .编译成位码(如果需要)

  当找到符合import语句的源代码文件后,如果有必要python接下来会将源代码编译成字节码(.pyc)。
  只有被导入的文件才会产生.pyc字节码文件,顶层文件是不会产生的。
  python 会通过实践戳来判断源代码是否有更新。如果只找到字节码,那么跳过编译。

3 .执行模块的代码来创建其定义的对象

 import操作的最后步骤是执行模块的字节码。文件所有语言都会依次执行,产生文件的属性。例如def 语句会在导入时创建     函数,
 此时模块就有了这个函数的属性。对于那些在顶层的语句,例如单独一行的print函数会在第一次导入时执行。
 所以我们建议如果是需要做顶层测试的语句通过__name__和__main__来设计。

对于已经导入过的文件(已经存在于sys.modules),再次使用import 语句时,上面三个步骤不会执行,python会加载内存内的模块.
可以通过下面的命令来查看已经导入的模块文件

import sys
list(sys.modules.keys())

import 和from 是赋值语句

因为无论使用import还是from语句,其本质都是赋值语句。
通过import语句的,调用变量时,需要使用module.x语句
通过from语句,是讲模块中的变量名赋值给另一个模块中的同名变量。
所以通过from语句赋值的变量名会变成共享对象的引用。因此根据此变量时可变(本地可以修改)还是不可变对象,针对变量的修改会影响到原有模块中的同名变量。

# pack_try0.py
x=1
y=[2,3]

通过GUI执行

>>> x=5
>>> y=[78]
>>> from pack_try0 import x,y
>>> x
1
>>> y
[2, 3]
>>> x=5
>>> y[0]=4
>>> y
[4, 3]
>>> import pack_try0
>>> pack_try0.x
1
>>> pack_try0.y
[4, 3]
>>> 
>>> y=99
>>> pack_try0.y
[4, 3]
>>> 
 此处可以看到,通过from语句导入的变量x,y覆盖了本地的x,y。因为from 是赋值。
 x的对象是1(数字)
 y的对象是[2,3](list),
 数字是不可改变的对象,list是可以本地修改的可变对象。
 所以对于x变量的修改(x=5),并没有修改原始文件中的值。
 对于变量y的修改(y[0]=4)改变了原始文件中的y值。因为此时y是引用了可变对象。
 对于y=99,因为此时本地的y引用了另一个对象99(数字),所以此时y已经不是对原有导入的文件中的y的引用了。
 不会影响原有文件中的y的值

模块包

除了能够导入某个模块名外,导入也可以指定目录路径,python代码的目录就成为包。这种导入目录就称为包导入。

包导入基础

  • 导入语法

    包导入中子目录中间是以点号相隔的。例如import dir1.dir2.mod
    
  • init.py包文件

     在包导入的每一级路径下都必须有一个__init__.py的文件。否则包导入会失败。
     作用:
       - 包的初始化
          python首次导入某个目录的时,会自动执行该目录内的__init__.py中所有程序代码
          例如我们的例子中如果import dir1 ,则会执行dir1下的__init__.py,如果import dir1.dir2则会执行
          dir1下的__init__.py 和dir2 下的__init__.py
       - 模块命名空间的初始化
         在包导入的时候,脚本内的目录路径,在导入后会变成真实的嵌套对象路径,即,import dir1,此时
         dir1变为了一个模块对象,此对象的命名空间内包含了所有在__init__文件中的所有变量。
         调用中可以用dir1.x调用__init__中的变量x
       - from*语句的行为
         __init__.py中可以实现所有的代码,即可以定义函数,变量等。但当有其它的正常模块的文件时,
         我们需要通过在__init__.py中使用__all__列表来定义目录中需要被导入的变量。因为通过包导入
         的时候,python只会导入__init__.py中的变量。当然我们可以在__init__.py中使用 import/from 
         来导入当前目录下其它的文件中的变量.
    

注意:init.py文件,如果你用不着也必须存在,你可以创建一个空的文件。

实例

包目录结构:
在python的搜索目录中,包含
– dir1
init.py
–dir2
init.py
–mod.py

在这里插入图片描述

#di11/__init__.py
print('dir1 init')
x=1
y=[1,2,3]


#dir1/di12/__init__.py
print('dir1/dir2 init')
z=1
m=[1,2,3]

#dir1/dir2/mod.py
print("dir1/dir2/mod.py")
n=4
  • 实例1 import dir1

    >>> import dir1
    dir1 init
    >>> dir1.x
    1
    >>> dir1.y
    [1, 2, 3]
    

    实例2 import dir1.dir2

    import dir1.dir2
    dir1 init
    dir1/dir2 init
    >>> dir1.x
    1
    >>> dir1.y
    [1, 2, 3]
    >>> dir1.dir2.m
    [1, 2, 3]
    >>> dir1.dir2.z
    1
    >>> dir1.dir2.mod.n
    Traceback (most recent call last):
    File "<pyshell#9>", line 1, in <module>
    dir1.dir2.mod.n
    AttributeError: module 'dir1.dir2' has no attribute 'mod'
    
  • 实例3

    #根据实例2,基础上
    import dir1.dir2.mod
    dir1/dir2/mod.py
    >>> dir1.dir2.mod.n
    4
    

上面的实例我们可以看到通过导入包的时候mod中的变量没有被导入。因为在__init__.py中看不到对应的变量。
那么可以通过我们所说的两种方式
1. __ all __

      __all__只对from  import *有用。
      例如我们把dir2/__init__改为
      这里使用的是module的名字作为__all__列表的某个值
#dir1/di12/__init__.py
__all__=['mod']
print('dir1/dir2 init')
z=1
m=[1,2,3]
 执行from dir1.dir2 import * 
 此时只有mod.n可以调用

2. 使用import在__init__.py中

改变dir2/__init__.py 为
这里的.mode中的.为当前目录(后面会解释这个用法)
#dir1/di12/__init__.py
#__all__=['mod']
from .mod import n
print('dir1/dir2 init')
z=1
m=[1,2,3]

**注意: 这里我们说的import dir1/import dir1/dir2/from dir1/dir2 import 等是基于通过外部文件导入当前包文件。 我们可以看到这里在__init__.py中使用from .dir1 import 等语句时基于包内的导入。这个时候的点号就是包相对导入的一种。下面我们会介绍。

通过外部访问包时

  • 此时的包路径必须在搜索路径内。(可以通过我们之前将的方式设置当前包为搜索路径内)

包相对导入

目前为止,通过包导入(import dir1.dir2)也可以用于包内导入,但是相对来说比较麻烦。包自身内部可以使用相对导入,是相对于包,而不是先列出包导入路径。

  相对导入实例:
  from . import xx
  from .dir2 import xx
  from .. import xx
  from ..dir2 import xx

相对导入的作用域

  • 相对导入适用于只在包内导入
  • 相对导入只是用于from语句
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值