22、模块和包

之前两天我们介绍了一些比较常用的模块,而我也说过会讲解什么是模块,今天我们就来分析分析模块和包,模块我们现阶段使用还可以而包的话现阶段我们基本很少会用到包,学的不是很清楚也没关系这些东西都是用的多了也就慢慢熟悉了。

 

本篇导航:

 

一、模块

在前面的几个章节中我们脚本上是用 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

 

posted @ 2017-08-09 19:46  布吉岛丶  阅读(738)  评论(0编辑  收藏  举报