五、模块、包、软件开发规范
目录
一、模块
二、包
三、软件开发规范
一、模块
1.什么是模块
最常见的场景,一个模块包含一组python文件,例如module.py,模块名是module
可以使用import module,有四个通用类别:
1*.使用python编写的.py文件 2.已被编译为共享库或DLL的C或C++扩展 3*.把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)* 4.使用编写并链接到python解释器的内置模块
2.为什么要用模块
1.从文件级别组织程序,更方便管理
2.拿来主义,提升开发效率
3.如何使用模块---》 import 模块名
1、第一次导入模块,会发生3件事:(重复导入只会引用之前加载好的结果)
1.主程序会产生一个模块的名称空间 2.运行模块文件代码,产生的名字都存放在模块的名称空间中,运行过程中global关键字指向的就是该名称空间 3.在当前名称空间拿到一个模块名,该模块名字指向模块的名称空间 引用spam.py中名字的方式:spam.名字
强调:被导入的模块在执行过程中使用自己独立的名称空间作为全局名称空间
#spam文件模块 print ('from the spam.py') money=1000 def read1(): print ('spam模块:',money) def read2(): print ('spam模块') read1() def change(): global money money=0
#无论用户输入哪种数据库,都希望命名为sql engine=input('>>>:') if engine == 'mysql': import mysql as sql elif engine == 'oracle': import oracle as sql
4.如何使用模块---》from 模块名 import 名字
优点:引用时不用加前缀,简单
缺点:容易与当前名称空间的名字冲突
from spam import *
注,如果import *,不想让用户访问模块内的某个名字,可以使用_名字,如在自定义模块spam.py里 全局变量不想被访问,可以_money
import * 其实是去模块里__all__找名字,默认是模块内所有名字,可以改写,如__all__=['read1','read2'],则表示只能读取模块内的read1,read2名字
5、一个python文件的两种用途
#1.当做脚本执行:__name__=='__main__' #2.当做模块被导入使用:__name__ =='模块名' if __name__ =='__main__': pass
6.模块的搜索路径
内存---->内置模块---->sys.path(环境变量)
将没有与主程序在同一目录下的模块,又不在sys.path里,可手动添加sys.path.append('模块路径')
sys.modules 可查看内存已经加载了哪些内置模块和自定义模块
7.避免模块的交叉调用
什么情况下会出现模块的交叉调用报错? 主程序 ---调用--->模块b 模块b ----调用---->主程序 若此时主程序有引用模块B变量,则产生交叉调用,报错 主程序test1.py import test2 x=10 print (test2.y) 模块test2.py import test1 print (test1.x) #不会报错 y=100 主程序执行,报错 AttributeError: module 'test2' has no attribute 'y'
二、包
2.1 什么是包
包就是一个包含了__init__.py文件的文件夹,(可以往该文件夹下放一堆子模块)
2.2 包下的__init__.py作用
import 包
python可以将包作为模块运行,而包本质是一个文件夹,因此需要一个.py的文件,因此import 包,本质上是运行from 包 import __init__
import 包,包是作为主程序下访问包.方法的变量名
2.3 包的使用
注意:但凡是在导入时,出现点(.),这是导入包才有的语法,点(.)的左边必须是一个包,使用的时候没有这种限制
包的示范文件
test.py #与glance包同一目录,作为主程序
glance/ #Top-level package ├── __init__.py #Initialize the glance package ├── api #Subpackage for api │ ├── __init__.py │ ├── policy.py │ └── versions.py ├── cmd #Subpackage for cmd │ ├── __init__.py │ └── manage.py └── db #Subpackage for db ├── __init__.py └── models.py
单独导入包名称时不会导入包中所有包含的所有子模块,如
#在与glance同级的test.py中 import glance glance.cmd.manage.main() ''' 执行结果: AttributeError: module 'glance' has no attribute 'cmd' '''
解决方法一(推荐):
#在glance/__init__.py文件里 from . import cmd #在glance/cmd/__init__.py文件里 from . import manage
#在test.py主程序文件里执行
import glance
glance.cmd.manage.main()
解决方法二:
#在glance/__init__.py文件里 from .cmd import manage #在test.py主程序文件里执行 import glance glance.cmd.manage.main()
2.4 包的使用之from ... import ...
需要注意的是from后import导入的模块,必须是明确的一个不能带点(.) , 否则会有语法错误,如:from a import b.c是错误语法
例子:
#写法一:在test.py主程序中 from glance.db import models models.register_models('mysql') #写法二:在test.py主程序中 from glance.db.models import register_models register_models('mysql')
2.5 from glance.api import *
在上面模块讲过,一个模块内导入所有*,是导入该模块的所有名称;
而包导入*,是表示导入该包目录下所有的子模块,不包含二级包下子模块
此处是想从包api中导入所有,实际上该语句只会导入包api下__init__.py文件中定义的名字(__all__定义),可以通过修改__all__来决定导入的名字,如
#在/glance/api/__init__.py文件里 x=10 def func(): print ('from api.__init__.py') __all__=['func','policy'] #在test.py文件里 from glance.api import * func() policy.get()
version.create_resource() #错误,因为version模块不在__all__列表里 print (x) #错误,因为x不在__all__列表里,因此报错显示无定义
练习:
#执行文件中的使用效果如下,请处理好包的导入 from glance import * get() create_resource('a.conf') main() register_models('mysql')
#在glance/__init__.py文件里 from .api.policy import * from .api.versions import * from .cmd.manage import * from .db.models import * #__all__可用可不用,默认是所有 __all__=['get','create_resource','main','register_models']
2.6 包的相对路径和绝对路径
我们的最顶级包glance是写给别人用的,然后在glance包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:
绝对导入:以glance作为起始
相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)一个点(.)为本级目录,两个点(..)返回上一级目录
例如:我们在glance/api/version.py中想要导入glance/cmd/manage.py
#在glance/api/version.py文件里: #绝对导入 from glance.cmd import manage manage.main() #相对导入(建议使用) from ..cmd import manage manage.main()
#在test.py主程序文件里:
from glance.api import versions
versions.create_resource('a.conf')
versions.manage.main()
2.7 包以及包所包含的模块都是用来被导入的,而不是被直接执行的。而环境变量都是以执行文件为准的
比如我们想在glance/api/versions.py中导入glance/api/policy.py,有的人一抽这俩模块是在同一个目录下,就直接这么做:
#在glance/api/version.py文件中 import policy policy.get() #在test.py文件中 from glance.api import versions ''' 执行结果: ImportError: No module named 'policy' '''
三、软件开发规范
#bin文件:存放主程序 #conf文件:存放配置文件 #core文件:存放核心代码 #db文件:存放数据库文件 #lib文件:存放自定义库 #log文件:存放日志记录文件 #ReadMe文件:程序功能使用介绍
#在ATM/bin/start.py文件 #入口:启动文件 import sys,os #BASE_DIR是项目根目录,os.path.dirname取目录,os.path.abspath取文件绝对路径,__file__程序名 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #将项目根目录加入环境变量 sys.path.append(BASE_DIR) from core import src if __name__ =='__main__': src.run() ======================================================== #在ATM/conf/文件里的setting.py #配置文件 import os #BASE_DIR是项目根目录 BASE_PATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #路径拼接,结果E:\python3_file\Pycharm3\ATM\log\access.log LOG_PATH=os.path.join(BASE_PATH,'log','access.log') DB_PATH=os.path.join(BASE_PATH,'db','user') print (LOG_PATH) ======================================================== #在ATM/core/src.py文件 #程序核心代码 #调用ATM/lib/common文件里的日志记录函数 from lib import common #调用ATM/lib/common文件里的sql数据库处理函数 from lib import sql def shop(): print ('购物ing....') def check_balance(): print ('查看余额....') #去数据库函数执行查询余额sql命令 res=sql.execute('select balance from user where id=3') print (res) def transfer(): print ('转账....') log_msg='bank给lisl转了1亿元' #调用日志功能记录日志 common.logger(log_msg) #主函数 def run(): msg=''' 1. 购物 2.查看余额 3.转账 ''' while True: print (msg) choice= input('>>>:').strip() if choice == '1': shop() elif choice =='2': check_balance() elif choice =='3': transfer() else: print ('输入错误') ======================================================== #在ATM/db/user文件里 lisl,18,male,100 zhangsan,28,female,10000 ======================================================== #在ATM/lib/common.py文件里: #自定义库 from conf import setting def logger(msg): #setting.LOG_PATH为ATM/conf/setting文件里定义的日志绝对路径 with open(setting.LOG_PATH,'a',encoding='utf-8') as f: f.write('%s\n'%msg) ======================================================== 在ATM/lib/sql.py文件里: #去ATM/conf/setting.py文件里找到DB_Path的路径 from conf import setting def execute(sql): print ('我是数据库处理函数') ======================================================== #在ATM/log/access.log文件里 bank给lisl转了1亿元