模块与包

模块

模块基础

模块就是一系列功能的集合体,也就是一堆函数的集合体。因此一个py文件就可以看成一个模块

我们的程序从面条型---》函数版本---》文件版(模块)---》文件夹版(包)

模块的分类

  1. 自定义模块
  2. 第三方模块
  3. 内置模块

import 与from...import...

我们定义好的模块是拿过来使用的,所以我们可以通过import与from...import...来导入模块

import模块

import time

time.sleep(1)

import导入函数会经过如下三个步骤(以上面函数为例):

  1. 先到开time模块文件
  2. python解释器运行time模块,然后把time模块名字放入time模块名称空间
  3. 主程序中就会有一个变量time指向time模块名称空间

from...import...模块

from time import sleep

sleep(1)

from...import...导入函数会经过如下三个步骤(以上面函数为例):

  1. 先到开time模块文件
  2. python解释器运行time模块,然后把time模块名字放入time模块名称空间
  3. 主程序中就会有一个变量sleep指向time模块名称空间里的sleep函数

循环导入问题

当我们写程序的时候,调用的模块比较频繁,不可避免模块之间的相互调用,这样就有可能出现循环导入问题

# m1.y
from m2 import y
x = 10


# m2.py
from m1 import x
y = 20

如上两个模块之间的相互调用就会出现循环导入问题

  1. m1文件导入m2文件的y
  2. m2文件导入m1文件的x
  3. 因为代码是自上而下运行的,所以在运行m1模块时,会去m2文件找y,但m2模块首先就是去m1模块找x,这样就又回到了m1模块,这样就陷入了循环导入,会报错

解决方案一

# m1.y
x = 10
from m2 import y


# m2.py
y = 20
from m1 import x

将需要调用的放到导入模块之前,这样m1,m2就能找到需要的x,y

但这样做,需要对所有变量都这么做,才能解决问题,没有完美的解决

解决方案二

# m1.y
def f1():
    from m2 import y
    
x = 10
f1()


# m2.py
def f2():
    from m1 import x
    
y = 10
f2()

将模块调用放入到函数中,这样全局变量就会先生成,函数会在文件运行的时候再生成

执行顺序:内置名称空间--》全局名称空间---》局部名称空间

模块的搜索路径

  1. 去内存中查找
# 验证办法

import m1   # 此时m1模块已在内存空间

import time

# 删除m1模块
time.sleep(10)
   
import m1    # 此时m1文件已不存在,但不报错,所以是在内存中找到了m1模块
  1. 去内置模块中找
# 验证办法

# time.py
print('from time')


import time   # 无任何打印,证明先去内置模块中找
  1. 环境变量中找
import sys

print(sys.path)

# b/a/m1.py

# b/test.py
import m1  # 报错

sys.path.append('b/a')
import m1   # 不报错

Python文件的两种用途

  1. 当作模块文件进行导入,可以有多个
  2. 当作运行文件,进行文件执行,只能有一个
# m1.py
def f1():
    print('from f1')
    
f1()

# test.py
import m1

m1.f1()  # 运行两次
  • _name_
# m1.py
def f1():
    print('from f1')
    
if __name__ == '__main__':  # __name__在m1.py被当做模块导入时是模块名,作为执行文件时是'__main__'
    f1()
    

# test.py
import m1

m1.f1()  # 运行一次

批量生成文件

import compileall
compileall.compile_dir('$dir')
其中,$dir 为Python源代码所在的目录。

什么是包

包是模块的一种形式,包的本质就是一个含有__init__.py的文件的文件夹。

为什么要有包

主要是因为当我们模块太大是,不能更好的去管理,所以可以把这个模块分为多个模块,但我们又不能改变模块的调用方式,就出现了包的概念

如何使用包

导入模块发生的三件事:

  1. 创建一个包的名称空间
  2. 执行py文件,将执行过程中产生的名字存放于名称空间中。
  3. 在当前执行文件中拿到一个名字aaa,aaa是指向包的名称空间的

导入包发生的三件事:

  1. 创建一个包的名称空间
  2. 由于包是一个文件夹,无法执行包,因此执行包下的__init__.py文件,将执行过程中产生的名字存放于包名称空间中(即包名称空间中存放的名字都是来自于__init__.py)
  3. 在当前执行文件中拿到一个名字aaa,aaa是指向包的名称空间的

导入包就是在导入包下的__init__.py,并且可以使用以下两种方式导入:

  1. import ...
  2. from ... import...
# aaa.py

def f1():
    pass

def f2():
    pass

def f3():
    pass

def f4():
    pass

def f5():
    pass

def f6():
    pass

def f7():
    pass

def f8():
    pass

以上aaa模块里面函数看起来就很多,我们把aaa模块分为三个模块

# m1.py

def f1():
    pass

def f2():
    pass

def f3():
    pass
# m2.py

def f4():
    pass

def f5():
    pass

def f6():
    pass

# m3.py

def f7():
    pass

def f8():
    pass
# aaa/__init__.py
from m1 import *
from m2 import *
from m3 import *
# run.py

from aaa import *
f1()
f2()
...

其实我们在导入aaa时其实是导入的aaa里面的__init__.py文件,这样我们就做到了在不改变调用方式的情况下把aaa.py模块变成了一个包

注意事项

  1. 包内所有的文件都是被导入使用的,而不是被直接运行的

  2. 包内部模块之间的导入可以使用绝对导入(以包的根目录为基准)与相对导入(以当前被导入的模块所在的目录为基准),推荐使用相对导入

  3. 当文件是执行文件时,无法在该文件内用相对导入的语法,只有在文件时被当作模块导入时,该文件内才能使用相对导入的语法

  4. 凡是在导入时带点的,点的左边都必须是一个包,import aaa.bbb.m3.f3错误

原文地址:https://www.cnblogs.com/Hades123/p/11019667.html