之前两天我们介绍了一些比较常用的模块,而我也说过会讲解什么是模块,今天我们就来分析分析模块和包,模块我们现阶段使用还可以而包的话现阶段我们基本很少会用到包,学的不是很清楚也没关系这些东西都是用的多了也就慢慢熟悉了。
本篇导航:
一、模块
在前面的几个章节中我们脚本上是用 python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。 这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用。
1、import
print('from the my_sp.py') money=1000 def read():
print('this read:',money) def read2():
print('this read2')
read()
自定义模块my_sp
模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载大内存中的模块对象增加了一次引用,不会重新执行模块内的语句), 我们可以从sys.module中找到当前已经加载的模块,sys.module是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否需要重新导入。
import my_sp #导入刚自己定义的模块
my_sp.read() #使用函数 import sys #调用sys模块
#作用:显示现在程序调用的所用模块
for i in sys.modules :
print(i)
import
2、命名空间
每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突
1)为源文件(my_sp模块)创建新的名称空间,在my_sp中定义的函数和方法若是使用到了global时访问的就是这个名称空间。
2)在新创建的命名空间中执行模块中包含的代码
3)创建名字my_sp来引用该命名空间
3、模块别名
import my_sp as sp
sp.read()
别名
使用实例:
1)有两中sql模块mysql和oracle,根据用户的输入,选择不同的sql功能
#mysql.py
def sqlparse():
print('from mysql sqlparse')
#oracle.py
def sqlparse():
print('from oracle sqlparse') #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)为已经导入的模块起别名的方式对编写可扩展的代码很有用,假设有两个模块xmlreader.py和csvreader.py,它们都定义了函数read_data(filename):用来从文件中读取一些数据,但采用不同的输入格式。可以编写代码来选择性地挑选读取模块
if file_format == 'xml':
import xmlreader as reader
elif file_format == 'csv':
import csvreader as reader
data=reader.read_date(filename)
示例用法2
4、from...import
这种形式是导入啥就能用啥,不导入的一律不能用,这个被import的名字就属于全局,可以节省空间。from 语句相当于import,也会创建新的名称空间,但是将my_sp中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了
from my_sp import read1 #可以直接使用read1
read1() #my_sp里面的其他方法和变量将不能再用
from...import
如果当前有重名read1那么会有覆盖效果。
也支持as
from my_sp import read1 as rd
5、from 模块 import *
首先会把模块当中所有不是‘_’开头的内容导入进来
还可以通过__all__来控制可以导入的内容(如果不使用__all__就可以导入所有名字)
但是 以上两条只和 * 有关
不建议使用
6、importlib
import time,importlib
import my_sp my_sp.read() time.sleep(10) # importlib.reload(my_sp)
my_sp.read() #在10秒的等待时间里,修改my_sp.py中read()的内容,等待程序的结果。 #打开importlib注释,重新测试 #importlib可以重新加载模块但是不建议使用
importlib
7、__name__
我们可以通过模块的全局变量__name__来查看模块名:
当做脚本运行:
__name__ 等于'__main__'
当做模块导入:
__name__= 模块名
作用:用来控制.py文件在不同的应用场景下执行不同的逻辑
if __name__ == '__main__':
8、模块搜索路径
python解释器在启动时会自动加载一些模块,可以使用sys.modules查看
解释器则会查找同名的内建模块,如果还没有找到就从sys.path给出的目录列表中依次寻找my_module.py文件。
模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块
import sys
print(sys.path)
#['G:\\python\\python代码\\八月\\day9 模块与包', 'G:\\python\\python代码', 'G:\\python\\Python36\\python36.zip', 'G:\\python\\Python36\\DLLs', 'G:\\python\\Python36\\lib', 'G:\\python\\Python36', 'G:\\python\\Python36\\lib\\site-packages']
我们自定义的模块名不应该与系统内置模块重名。因为排在前的目录,优先被搜索这样我们就无法导入内置模块。python会把.zip归档文件当成一个目录去处理。
9、编译python文件
为了提高加载模块的速度,强调:提高的是加载速度而绝非运行速度。python解释器会在__pycache__目录中下缓存每个模块编译后的版本,格式为:__pycache__/my_sp.cpython-36.pyc。这种命名规范保证了编译后的结果多版本共存。(36为版本)
它的出现仅仅是用来提升模块的加载速度的。
python -m my_sp.py
提示:
1)模块名区分大小写
2)在速度上从.pyc文件中读指令来执行不会比从.py文件中读指令执行更快,只有在模块被加载时,.pyc文件才是更快的
3)个人认为没有必要去单独编译,因为当你import导入模块时会自动编译
10、dir()
内建函数dir是用来查找模块中定义的名字,返回一个有序字符串列表
import my_sp
print(dir(my_sp))
二、包
包个人概括:
1)无论是import形式还是from...import形式,凡是在导入语句中使用点的(一般是表示相对路径)
2)包的本质就是一个包含__init__.py文件的目录
3)导入包本质就是在导入该文件
4)包只是模块的一种形式而已,包即模块(包为文件夹,模块为文件)
import os
os.makedirs('glance/api')
os.makedirs('glance/cmd')
os.makedirs('glance/db')
l = []
l.append(open('glance/__init__.py','w'))
l.append(open('glance/api/__init__.py','w'))
l.append(open('glance/api/policy.py','w'))
l.append(open('glance/api/versions.py','w'))
l.append(open('glance/cmd/__init__.py','w'))
l.append(open('glance/cmd/manage.py','w'))
l.append(open('glance/db/__init__.py','w'))
l.append(open('glance/db/models.py','w'))
map(lambda f:f.close() ,l)
创建包目录
#文件内容(在相应的文件里输入以便测试) #policy.py
def get():
print('from policy.py') #versions.py
def create_resource(conf):
print('from version.py: ',conf) #manage.py
def main():
print('from manage.py') #models.py
def register_models(engine):
print('from models.py: ',engine)
文件内容
1、import
import glance.db.models
glance.db.models.register_models('hello')
2、from...import
from后import导入的模块,必须是明确的一个不能带点
#from glance.db import models.register_models #报错 from glance.db.models import register_models
register_models("hello")
3、__int__文件
不管是哪种方式,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py文件。这个文件可以为空,但是也可以存放一些初始化包的代码。
4、from glance.api import *
在讲模块时,我们已经讨论过了从一个模块内导入所有*,包也是模块那么我们也研究从一个包导入所有*。
此处是想从包api中导入所有,实际上该语句只会导入包api下__init__.py文件中定义的名字,我们可以在这个文件中定义__all___:
#在api文件夹下的__init__.py文件中定义
__all__=['policy','versions']
5、绝对导入和相对导入
绝对导入:以glance作为起始
相对导入:用.或者..的方式最为起始(.为本层目录,..为上层目录)
#在api目录下的文件内:
from . import policy #.表示当前目录
from ..cmd import manage #..表示上一级目录,想再api目录下的文件内中使用manage中的方法就需要回到上一级glance目录往下找cmd包,从cmd导入manage
注意:
相对导包,包程序不能单独运行会报错。只能由包外部导入使用。简单说就是导包时有from后面直接跟.的不能直接运行
6、单独导入包
import glance
glance.cmd.manage.main()
#报错
解决方法:
#glance/__init__.py
from . import cmd #glance/cmd/__init__.py
from . import manage #api,db同理
单独导入包解决1
#glance/__init__.py
from .api import *
from .cmd import *
from .db import * #glance/cmd/__init__.py
__all__ = ['manage'] #api,db同理
单独导入包解决2
小知识:
软件开发规范:
#=============>bin目录:存放执行脚本
#start.py #=============>conf目录:存放配置文件
#config.ini
#my_log_settings.py
#settings.py #=============>core目录:存放核心逻辑
#core.py #=============>db目录:存放数据库文件
#alex_json
#egon_json #=============>lib目录:存放自定义的模块与包
#read_ini.py #=============>log目录:存放日志
#all2.log