模块
一:模块
1.什么是模块?
模块是一组功能的集合体,程序可以导入模块来复用模块里的功能。
模块分为四个通用的类别:
1,使用python编写的文件
2,已被编译为共享库或DLLdC或者C++扩展
3,把一系列模块组织到一起的文件(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
4,使用C编写并链接到python解释器d内置模块
2.为何要用模块?
1,从文件级别组织程序,更方便管理
2,拿来主义,提升开发效率
3.怎么用模块?
一:import
3.1import的使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句),如下 #test.py import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py',当然其他的顶级代码也都被执行了,只不过没有显示效果. import spam import spam import spam ''' 执行结果: from the spam.py '''
3.2在第一次导入模块时会做三件事,重复导入会直接引用内存中已经加载好的结果
1:会产生一个模块的名称空间
2:执行xxx.py文件的内容,将产生的名字丢到模块的名称空间
3:在当前执行文件中拿到名字read1,该名字指向模块的名称空间中的read1
3.3被导入模块有独立的名称空间
每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当作全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突。
3.4为模块起别名
1 import spam as sm
2 print(sm.money)
3.5在一行导入多个模块
import sys,os,re
二:from...import...
1.from...import...的使用
from spam import read1,read2
2.使用from...import...,将spam中的名字直接导入到当前的名称空间中,所以在当前的名称空间中,直接使用名字就可以了,无须加前缀:.spam。
3.也支持as
from spam imort read1 as read
4.一行导入多个名字
from spam import read1,read2,money
5.from...import*
from...import*将spam中所有的不是以下划线(_)开头 的名字导入到当前位置。大部分情况下我们的python出现不应该使用这种导入方式,因为不知道导入了什么名字,很有可能会覆盖掉之前已经定义的名字。而且可读性极其差,在交互式环境中导入没有问题。
可以使用__all__来控制*(用来发布新版本),在spam.py中新增一行
__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字
6.与import相比
import:
优点:引用模块中的名字必须加前缀(模块名),不会与当前执行文件名称空间中的名字冲突
缺点:引用模块中的名字必须加前缀(模块名),使用不够简洁
from...import...:
优点:引用模块中的名字不用加前缀(模块名),使用更为简洁
缺点:容易与当前执行文件名称空间中的名字冲突
二:模块导入问题
模块循环/嵌套导入抛出异常的根本原因是由于在python中模块被导入一次之后,就不会重新导入,只会在第一次导入时执行模块内代码。
在我们的项目中应该尽量避免出现循环/嵌套导入,如果出现多个模块都需要共享数据,可以将共享的数据集中存放到某一个地方。
三:py文件区分两种用途:模块与脚本
#编写好的一个python文件可以有两种用途:
一:脚本,一个文件就是整个程序,用来被执行
二:模块,文件中存放着一堆功能,用来被导入使用
#python为我们内置了全局变量__name__,
当文件被当作脚本执行时:__name__等于'__main__'
当文件被当作模块导入时:__name__等于模块名
#作用:用来控制.py文件在不同应用场景下执行不同的逻辑
if __name__ == '__main__':
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#fib.py def fib(n): # write Fibonacci series up to n a, b = 0, 1 while b < n: print(b, end=' ') a, b = b, a+b print() def fib2(n): # return Fibonacci series up to n result = [] a, b = 0, 1 while b < n: result.append(b) a, b = b, a+b return result if __name__ == "__main__": import sys fib(int(sys.argv[1])) #执行:python fib.py <arguments> python fib.py 50 #在命令行
四:模块的搜索路径
模块的查找顺序是:内存中已经加载的模块——>内置模块——>sys.path路径包含的模块
在程序出现循环/嵌套导入后的异常分析,解决方法如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#示范文件内容如下 #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/liuxiaolu/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> from m2 import y File "/Users/liuxiaolu/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module> from m1 import x File "/Users/liuxiaolu/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() 示例文件
五:软件开发规范
bin:用来放程序执行文件;start.py
conf:配置文件
log:日志文件
lib:放模块和包
db:数据文件
core:放程序的核心逻辑,里面src.py
以上非硬性规定!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#模块的查找顺序 1、在第一次导入某个模块时(比如spam),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用 ps:python解释器在启动时会自动加载一些模块到内存中,可以使用sys.modules查看 2、如果没有,解释器则会查找同名的内建模块 3、如果还没有找到就从sys.path给出的目录列表中依次寻找spam.py文件。 #sys.path的初始化的值来自于: The directory containing the input script (or the current directory when no file is specified). PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH). The installation-dependent default. #需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错。 #在初始化后,python程序可以修改sys.path,路径放到前面的优先于标准库被加载。 >>> import sys >>> sys.path.append('/a/b/c/d') >>> sys.path.insert(0,'/x/y/z') #排在前的目录,优先被搜索 注意:搜索时按照sys.path中从左到右的顺序查找,位于前的优先被查找,sys.path中还可能包含.zip归档文件和.egg文件,python会把.zip归档文件当成一个目录去处理, #首先制作归档文件:zip module.zip foo.py bar.py import sys sys.path.append('module.zip') import foo,bar #也可以使用zip中目录结构的具体位置 sys.path.append('module.zip/lib/python') #windows下的路径不加r开头,会语法错误 sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a') #至于.egg文件是由setuptools创建的包,这是按照第三方python库和扩展时使用的一种常见格式,.egg文件实际上只是添加了额外元数据(如版本号,依赖项等)的.zip文件。 #需要强调的一点是:只能从.zip文件中导入.py,.pyc等文件。使用C编写的共享库和扩展块无法直接从.zip文件中加载(此时setuptools等打包系统有时能提供一种规避方法),且从.zip中加载文件不会创建.pyc或者.pyo文件,因此一定要事先创建他们,来避免加载模块是性能下降。