1、模块的介绍与使用模块import
1.1、模块的介绍
1.1.1、什么是模块?
模块就是一组功能的集合体,我们的程序可以导入模块来复用模块里的功能。
在python中,模块的使用方式都是一样的,但其实细说的话,模块可以分为四个通用类别:
(1)使用python编写的.py文件
(2)已被编译为共享库或DLL的C或C++扩展
(3)把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
(4)使用C编写并链接到python解释器的内置模块
模块有三种来源:
(1)自带的模块 内置模块 && 标准库
(2)第三方模块 pip3 install requests
(3)自定义的模块
常见的场景:一个模块就是一个包含了一组功能的python文件,比如spam.py,模块名为spam,可以通过import spam使用。
1.1.2、为何要使用模块
1、从文件级别组织程序,更方便管理
随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用
2、拿来主义,提升开发效率
同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种拿来主义,可以极大地提升我们的开发效率
如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。
1.1.3、实例
以spam.py为例来介绍模块的使用:文件名spam.py,模块名spam
#spam.py
print('from the spam.py')
money=1000
def read1():
print('spam模块:',money)
def read2():
print('spam模块')
read1()
def change():
global money
money=0
1.2、使用模块之import
1.2.1、import的使用
模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句),如下
test.py
import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.
import spam
import spam
import spam
#result:
from the spam.py
1.2.2、在第一次导入模块时会做三件事,重复导入会直接引用内存中已经加载好的结果
执行文件run.py自定义模块spam.py
(1)执行run.py文件,在被导入的spam.py模块所在文件中执行;
(2)产生spam.py的名称空间(run.py的名称空间),将spam.py运行过程中产生的名字都丢到新产生的模块spam的名称空间中;
(3)在当前文件中产生一个名字spam,该名字指向spam.py的名称空间
#总的来说:
(1)会触发模块文件的运行,产生一个模块的名称空间,将运行模块文件过程中产生的名字都丢到模块的名称空间
(2)在当前名称空间中产生一个名字spam,该名字指向模块的名称空间
1.2.3、被导入模块有独立的名称空间
每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突
测试一:money与spam.money不冲突
test.py
import spam
def read1():
print('========')
spam.read1()
执行结果:
from the spam.py
spam->read1->money 1000
测试二:read1与spam.read1不冲突
test.py
import spam
def read1():
print('========')
spam.read1()
执行结果:
from the spam.py
spam->read1->money 1000
测试三:执行spam.change()操作的全局变量money仍然是spam中的
test.py
import spam
money=1
spam.change()
print(money)
#print(spam.money) 输出的结果就是0
执行结果:
from the spam.py
1
1.2.4、为模块名起别名
为已经导入的模块起别名的方式对编写可扩展的代码很有用
import spam as sm
print(sm.money)
有两中sql模块mysql和oracle,根据用户的输入,选择不同的sql功能
(1)mysql.py
def sqlparse():
print('from mysql sqlparse')
(2)oracle.py
def sqlparse():
print('from oracle sqlparse')
(3)test.py
db_type=input('>>: ')
if db_type == 'mysql':
import mysql as db
elif db_type == 'oracle':
import oracle as db
db.sqlparse()
1.2.5、在一行导入多个模块
import sys,os,re
2、使用模块之from...import...
2.1、from...import...的使用
from spam import read1,read2
2.2、from...import 与import的对比**
唯一的区别就是:使用from...import...则是将spam中的名字直接导入到当前的名称空间中,所以在当前名称空间中,直接使用名字就可以了、无需加前缀:spam.
from...import...的方式有好处也有坏处
好处:使用起来方便了
坏处:容易与当前执行文件中的名字冲突
以spam.py为例来介绍模块的使用:文件名spam.py,模块名spam
#spam.py
print('from the spam.py')
money=1000
def read1():
print('spam模块:',money)
def read2():
print('spam模块')
read1()
def change():
global money
money=0
验证一:当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间
测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
#test.py
from spam import read1
money=200
read1()
执行结果:
from the spam.py
spam->read1->money 1000
测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
print('==========')
read2()
执行结果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
验证二:如果当前有重名read1或者read2,那么会有覆盖效果。
测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
#test.py
from spam import read1
def read1():
print('==========')
read1()
'''
执行结果:
from the spam.py
==========
验证三:导入的方法在执行时,始终是以源文件为准的
from spam import money,read1
money=100 #将当前位置的名字money绑定到了100
print(money) #打印当前的名字
read1() #读取spam.py中的名字money,仍然为1000
'''
from the spam.py
100
spam->read1->money 1000
2.3、支持as,一行导入多个名字
from spam import read1 as read
from spam import read1,read2,money
2.4、from...import *
from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置
大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。
from spam import * #将模块spam中所有的名字都导入到当前名称空间
print(money)
print(read1)
print(read2)
print(change)
执行结果:
from the spam.py
1000
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>
可以使用__all__来控制*(用来发布新版本),在spam.py中新增一行 __all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字
2.5、模块循环导入问题
模块循环/嵌套导入抛出异常的根本原因是由于在python中模块被导入一次之后,就不会重新导入,只会在第一次导入时执行模块内代码
#在我们的项目中应该尽量避免出现循环/嵌套导入,如果出现多个模块都需要共享的数据,可以将共享的数据集中存放到某一个地方.
在程序出现了循环/嵌套导入后的异常分析、解决方法如下:
示范文件内容如下:
m1.py
print('正在导入m1')
from m2 import y
x='m1'
m2.py
print('正在导入m2')
from m1 import x
y='m2'
run.py
import m1
测试一(已经被导入的模块,执行时不会重新导入)
执行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'
测试一结果分析:
先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"
--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错
测试二(执行文件不等于导入文件)
测试二:执行文件不等于导入文件,比如执行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'
测试二结果分析
执行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中所以报错。
解决方案(两种)
方法一:导入语句放到最后
m1.py
print('正在导入m1')
x='m1'
from m2 import y
m2.py
print('正在导入m2')
y='m2'
from m1 import x
方法二:导入语句放到函数中
m1.py
print('正在导入m1')
def f1():
from m2 import y
print(x,y)
x = 'm1'
# f1()
m2.py
print('正在导入m2')
def f2():
from m1 import x
print(x,y)
y = 'm2'
run.py
import m1
m1.f1()
小测试(使用import调用的时候,必须要跟上被调用的值)
m1.py
print('正在导入m1')
import m2
x='m1'
print(m2,y)
m2.py
print('正在导入m2')
import m1
y='m2'
run.py
import m1