模块与包

模块的简介

在Python中,一个py文件就是一个模块,文件名为xxx.py模块名则是xxx,导入模块可以引用模块中已经写好的功能。如果把开发程序比喻成制造一台电脑,编写模块就像是在制造电脑的零部件,准备好零部件后,剩下的工作就是按照逻辑把它们组装到一起。

将程序模块化会使得程序的组织结构清晰,维护起来更加方便。比起直接开发一个完整的程序,单独开发一个小的模块也会更加简单,并且程序中的模块与电脑中的零部件稍微不同的是:程序中的模块可以被重复使用。所以总结下来,使用模块既保证了代码的重用性,又增强了程序的结构性和可维护性。另外除了自定义模块外,我们还可以导入使用内置或第三方模块提供的现成功能,这种“拿来主义”极大地提高了程序员的开发效率。

模块分为内置模块、第三方模块、自定义模块

模块导入的方式

import语句

导入模块只需要一次就可以,即使导入多次也只执行一次。

import md

导入模块发生了哪些事呢?

1.首先会运行执行文件,产生执行文件的全局名称空间

2.运行导入文件

3.产生导入文件的全局名称空间,把导入文件中的名字全部放入全局名称空间去

4.会在执行文件中产生一个名字md指向导入文件的名称空间

若要引用模块名称空间的名字,需要加上前缀

import md#引入模块md
a=md.x   #引用模块md中的变量x的值赋值给当前名称空间中的名字a
md.get() #引用模块md中的get函数

用import语句导入多个模块,可以写多行import语句

import module1
import module2
    ...
import moduleN

还可以在一行导入,用逗号分隔开不同的模块

import get,read2,change

但其实第一种形式更为规范,可读性更强,推荐使用,而且我们导入的模块中可能包含有python内置的模块、第三方的模块、自定义的模块,为了便于明显地区分它们,我们通常在文件的开头导入模块,并且分类导入,一类模块的导入与另外一类的导入用空行隔开,不同类别的导入顺序如下

# 1. python内置模块
# 2. 第三方模块
# 3. 程序员自定义模块

当然,我们也可以在函数内导入模块,对比在文件开头导入模块属于全局作用域,在函数内导入的模块则属于局部的作用域。 

from ...import...

导入模块也会执行导入文件,多次导入也只会执行一次。

如果使用from...import...句式的情况,在执行文件中会出现名字冲突的情况(在执行文件中出现了和导入的名字一样的时候), 出现冲突的时候离谁近用谁的。

所有的导入语句都写在文件的开头

另外from语句支持from foo import 语法,代表将foo中所有的名字都导入到当前位置。

from foo import * # 把foo中所有的名字都导入到当前执行文件的名称空间中,在当前位置直接可以使用这些名字

a=x
get()
change()
obj=Foo()

如果我们需要引用模块中的名字过多的话,可以采用上述的导入形式来达到节省代码量的效果,但是需要强调的一点是:只能在模块最顶层使用的方式导入,在函数内则非法,并且的方式会带来一种副作用,即我们无法搞清楚究竟从源文件中导入了哪些名字到当前位置,这极有可能与当前位置的名字产生冲突。 

模块的其他用法补充

import foo as f #为导入的模块foo在当前位置起别名f,以后再使用时就用这个别名f
f.x
f.get()

循环导入问题

循环导入问题是指一个模块在加载或者导入过程中导入另一个模块,而在另一个模块中又返回来导入第一个模块的名字,由于第一个模块尚未加载完毕,所以引用失败抛出异常。这是因为在python中,同一个模块只会在第一次执行其内部代码,再次导入该模块时,即便是该模块尚未加载完毕也不会重复去执行内部代码。

m1.py
#假设这是m1.py文件
print('正在导入m1')
from m2 import y
x='m1'

m2.py
#假设这是m1.py文件
print('正在导入m2')
from m1 import x
y='m2'


run.py#这是执行文件
import m1

 结果1

#1、执行run.py会抛出异常
正在导入m1
正在导入m2
Traceback (most recent call last):
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module>
    import m1
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
    from m1 import x
ImportError: cannot import name 'x'
    
#2、分析
先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"
--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,
由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错

结果2

# 1、执行文件不等于导入文件,比如执行m1.py不等于导入了m1
直接执行m1.py抛出异常
正在导入m1
正在导入m2
正在导入m1
Traceback (most recent call last):
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
    from m1 import x
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
    from m2 import y
ImportError: cannot import name 'y'
    
# 2、分析
执行m1.py,打印“正在导入m1”,执行from m2 import y ,导入m2进而执行m2.py内部代码--->打印"正在导入m2",执行from m1 import x,此时m1是第一次被导入,执行m1.py并不等于导入了m1,于是开始导入m1并执行其内部代码--->打印"正在导入m1",执行from m1 import y,由于m1已经被导入过了,所以无需继续导入而直接问m2要y,然而y此时并没有存在于m2中所以报错

在你编码的过程中,循环导入是不能允许出现的
如果你的代码出现了循环导入问题,那就是你的代码设计不合理,及时更正

执行文件

if __name__ == '__main__':
在这个判断里面写的代码,只有是以执行文件运行的时候,才会被执行,当被当成导入文件的时候,不会被运行

名字的查找顺序

1.先从内存中查找

2.再从内置模块中去找

3.最好去环境变量sys.path中去找

以上都找不到就直接报错

注意,给文件或者模块起名字的时候一定不要和内置模块重名

sys.path中的路径

import sys
print(sys.path)

['D:\\Python27\\day20', 'D:\\Python27\\day20', 'D:\\PyCharm 2021.1.3\\plugins\\python\\helpers\\pycharm_display', 'D:\\Python38\\python38.zip', 'D:\\Python38\\DLLs', 'D:\\Python38\\lib', 'D:\\Python38', 'C:\\Users\\oldboy\\AppData\\Roaming\\Python\\Python38\\site-packages', 'D:\\Python38\\lib\\site-packages', 'D:\\PyCharm 2021.1.3\\plugins\\python\\helpers\\pycharm_matplotlib_backend']

 上面的列表中第一个元素永远是当前文件所在的路径

找模块的两种方式

1.把模块所在的路径添加到sys.path中

import sys
sys.path.append(r'D:\Python27\day20\aaa\bbb')

 2.利用from...import句式

from aa.bb.cc import m1

相对导入与绝对导入

在导入模块的时候,模块的查找始终是以执行文件所在的路径为准

绝对导入,始终以执行文件所在的sys.path路径为基准查找

相对导入,可以打破始终以执行文件的查找顺序

                .  代表的是当前路径

from .ccc import m2

包的使用

随着模块数量的增多,把模块不加区分的放在一起很不合理,于是python为我们提供了种把 模块 组织到一起的方法,就是创建一个包。包就是含有__init__.py文件的文件夹

pool/                #顶级包
├── __init__.py     
├── futures          #子包
│   ├── __init__.py
│   ├── process.py
│   └── thread.py
└── versions.py      #子模块

创建包的目的不是运行,而是被导入使用,包只是模块的一种形式,本质还是模块

包属于模块的一种,首次导入包会做三件事情:

1.执行包下的__init__.py文件

2.产生一个新的名称空间用于存放__init__.py执行过程中产生的名字

3.在执行文件所在的名称空间中得到一个名字pool,该名字指向__init__.py的名称空间 ,例如pool.xxx和pool.yyy中的xxx和yyy都是来自于pool下的__init__.py,也就是说导入包时并不会导入包下所有的子模块与子包

import pool

pool.versions.check() #抛出异常AttributeError
pool.futures.process.ProcessPoolExecutor(3) #抛出异常AttributeError

pool.versions.check()要求pool下有名字versions,而pool.versions下有名字check。但是pool下没有名字versions,这就需要在pool下的__init__.py中导入模块versions

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值