模块、循环导入、模块的搜索路径

时间:2021-05-19 22:14:39

模块

模块类型

  • 自定义模块:自己写一个py文件,在文件内写入一堆函数,则它被称为自定义模块,即使用python编写的.py文件
  • 第三方模块:已被编译为共享库或DLL的C或C++扩展
  • 内置函数:使用C编写并链接到python解释器的内置模块
  • 包:把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)

为什么要用模块

1.用第三方或则内置的模块是一种拿来主义,可以极大地提升开发效率

2.自定义模块,将我们自己程序中用到的公共功能,写入一个python文件,然后程序的各部分组件可以通过导入的方式来引用自定义模块的功能

如何使用模块

一般我们使用import和from...import...来导入模块

import模块名

import首次导入模块发生了3件事:

1.以模块为准创造一个模块的名称空间

2.执行模块对应的文件,将执行过程中产生的名字都丢到模块的名称空间

3.在当前执行文件中拿到一个模块名

import spam
import spam

模块的重复导入会直接引用之前创造好的结果,不会重复执行模块的文件,即重复导入会执行:spam=spam=模块名称空间的内存地址

from 模块名 import 具体功能

from time import sleep

from...import...首次导入模块发生了3件事:

  1. 以模块为准创造一个模块的名称空间
  2. 执行模块对应的文件,将执行过程中产生的名字都丢到模块的名称空间
  3. 在当前执行文件的名称空间中拿到一个名字,该名字直接指向模块中的某一个名字,意味着可以不用加任何前缀而直接使用
  • 优点:不用加前缀,代码更加精简
  • 缺点:容易与当前执行文件中名称空间中的名字冲突

导入文件内所有的功能:

__all__ = ['money', 'read1']  # 只允许导入'money'和'read1'
from spam import *  # 导入spam.py内的所有功能,但会受限制于__all__

import和from...import...的异同

相同点:

  1. 两者都会执行模块对应的文件,两者都会产生模块的名称空间
  2. 两者调用功能时,需要跑到定义时寻找作用域关系,与调用位置无关

不同点:

import需要加前缀;from...import...不需要加前缀

循环导入问题

# m1.py
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的名称空间找到y,但是找y前,需要运行m2的代码,m2的代码第一句是去找m1的x,然后又回去运行m1的代码,m1的第一行代码又是去寻找m2的y
  4. 就是说m1只能运行第一行,m2也只能运行第一行,所以出现了循环导入问题

解决方案一

# m1.py
x = 10
from m2 import y

# m2.py
y = 20
from m1 import x

但是这样做,对所有的变量都需要这样做,才能解决问题,所以治标不治本

解决方案二

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

f1()


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

f2()

名字的执行顺序:内置(python解释器启动的时候)-->全局(文件执行的时候)-->局部(函数调用的时候)

模块的搜索路径

去内存中找

# test.py
import m1  # 从m1.py文件中导入的,然后会生成m1模块的名称空间
import time

# 删除m1.py文件,m1模块的名称空间仍然存在

time.sleep(10)

import m1  # 不报错,一定不是从文件中获取了m1模块,而是从内存中获取的

去内置模块中找

# time.py
print('from time')

# test.py
import time  # 无任何打印,所以他先去内置模块中找了

环境变量中找

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()  # 运行两次