一、模块
例子:调用configpaser模块时,首先在当前的Python编译器去查找,找到后,里面没有ConfigParser()这个方法,直接报错,configparser应该让它去系统查找import configparserconfig = configparser.ConfigParser()
在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。
为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Python中,一个.py文件就称之为一个模块(Module)。使用模块有什么好处?最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括Python内置的模块和来自第三方的模块。使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。点这里查看Python的所有内置函数。你也许还想到,如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。俗解:代码越写越多,可以使用分组来管理,如把时间相关的放入一个组,以后要调用时间就通过time模块都取,随机数相关的去random取,一层层的取,把功能分开1、模块分为三种
- python标准库- 第三方模块(非官方)- 应用程序自定义模块(自定义py文件)2、Python模块的调用
简单例子:[root@python3 module]# cat calculate.py #!/usr/local/python3/bin/python3def add(x,y): return x+ydef sub(x,y): return x-y[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3import calculate ##调用print(calculate.add(1,2))[root@python3 module]# python3 bin.py 3
3、模块的搜索路径
[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3import sys ##调用sys模块查看import calculate print(calculate.add(1,2))print(sys.path) ##sys.path方法[root@python3 module]# [root@python3 module]# python3 bin.py #第一个路径就是当前的目录下module3['/root/py/fullstack_s2/week5/module', '/usr/local/python3/lib/python36.zip', '/usr/local/python3/lib/python3.6', '/usr/local/python3/lib/python3.6/lib-dynload', '/usr/local/python3/lib/python3.6/site-packages']
二、分析调用过程和方法
1.实际是Python解释器通过搜索路径把到calculate后,python解释器calculate所有代码都解释一次,无论是什么内容都可以通calculate去引用,调用函数中的add或sub或变量时,是通过calculate这个名字去调用方法[root@python3 module]# cat calculate.py #!/usr/local/python3/bin/python3print('cal is ok') ##打印def add(x,y): return x+ydef sub(x,y): return x-y[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3import sysimport calculate ##只是调用[root@python3 module]# python3 bin.py cal is ok
变量的调用也是要通过calculate调用
[root@python3 module]# cat calculate.py #!/usr/local/python3/bin/python3print('cal is ok')x = 4 ##def add(x,y): return x+ydef sub(x,y): return x-y[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3import sysimport calculate print(x) ###直接调用x[root@python3 module]# python3 bin.py cal is okTraceback (most recent call last): File "bin.py", line 4, inprint(x)NameError: name 'x' is not defined ##未定义[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3import sysimport calculate print(calculate.x) ##通过calculate的方法调用正常[root@python3 module]# python3 bin.py cal is ok4
import多个模块
import time,sys
2.从模块中调用某个方法(from module import func)
场景:模块文件里有很多函数,通过import相当于把所有的都加载一次,可以只调用某个函数来提高使用效率[root@python3 module]# cat calculate.py #!/usr/local/python3/bin/python3print('cal is ok')x = 4def add(x,y): return x+ydef sub(x,y): return x-y[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3from calculate import add #从calculate中调用addprint(add(2,3)) #直接使用函数[root@python3 module]# python3 bin.py cal is ok5[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3from calculate import add,sub ##同时调用多个print(add(2,3))print(sub(9,3))[root@python3 module]# python3 bin.py cal is ok56[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3from calculate import add,sub #通过from指定调用时,没有调用x,会报错,表示python并没加载xprint(add(2,3))print(sub(9,3))print(x)[root@python3 module]# python3 bin.py cal is ok56Traceback (most recent call last): File "bin.py", line 5, inprint(x)NameError: name 'x' is not defined ###[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3from calculate import add,subprint(add(2,3))print(sub(9,3))print(calculate.x) ##通过calculate去调用,但是还是报错,calculate没被定义,证明只是加载了add,sub[root@python3 module]# python3 bin.py cal is ok56Traceback (most recent call last): File "bin.py", line 5, in print(calculate.x)NameError: name 'calculate' is not defined ###
3.调用所有的方法,但是不使用模块名称去调用方法(from import *建议少用)
[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3 from calculate import * #导入所有print(add(4,9))print(x)print(sub(3,9))[root@python3 module]# python3 bin.py cal is ok134-6
问题分析:from module import *存在的的问题,可能会有以下场景,calculate中有add函数,而调用的Py中也有同样的add函数,实际python解释器是从上到下执行,从from开始,导入模块就加载了def add,而本文件中也有一个def add,所以类似于在同一个Py上给一个变量进行先后赋值,后赋值的生效,*号是加载所有的函数,可能会造成调用的函数与同py中所写的函数的产生一个冲突
[root@python3 module]# cat calculate.py #!/usr/local/python3/bin/python3print('cal is ok')x = 4def add(x,y): return x+ydef sub(x,y): return x-y[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3from calculate import *def add(x,y): return x+y+9print(add(4,5))[root@python3 module]# python3 bin.py cal is ok18
类似同一个py加载一个函数
In [3]: def f(): ...: print('f1') ...: In [4]: def f(): ...: print('f2') ...: In [5]: f()f2
from module import *表示解释器把要加载都执行一次,即使调用多次
[root@python3 module]# cat calculate.py #!/usr/local/python3/bin/python3print('cal is ok') ##print('cal2 is ok') ##x = 4def add(x,y): return x+ydef sub(x,y): return x-y[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3from calculate import * #调用两次from calculate import * #[root@python3 module]# python3 bin.py cal is ok #只执行一次cal2 is ok
4.别名的使用as
[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3from calculate import add as plusprint(plus(4,8))[root@python3 module]# python3 bin.py 12
三、包package
描述:模块为了组织函数,模块多了要组织模块,为了避免模块名的冲突,python引入了按目录来组织模块的方法[root@python3 module]# tree web/web/ #存放模块├── __init__.py #区分是文件玩夹还是包├── logger.py└── main.py[root@python3 week5]# tree module/module/├── bin.py #bin与web在同一层函数├── calculate.py└── web ├── __init__.py ├── logger.py └── main.py
通过bin去调用web中的模块
[root@python3 week5]# cat module/web/logger.py#!/usr/local/python3/bin/python3def logger(): print('logging in logger')[root@python3 week5]# cat module/bin.py #!/usr/local/python3/bin/python3from web import logger ##from package import modulelogger.logger()[root@python3 week5]# python3 module/bin.pylogging in logger
两层目录的情况
[root@python3 week5]# mkdir module/web/web2[root@python3 week5]# touch module/web/web2/__init__.py[root@python3 week5]# mv module/web/logger.py module/web/web2/[root@python3 week5]# tree module/module/├── bin.py├── calculate.py└── web ├── __init__.py ├── main.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ └── logger.cpython-36.pyc └── web2 ├── __init__.py └── logger.py[root@python3 week5]# cat module/bin.py#!/usr/local/python3/bin/python3from web.web2 import logger ##使用点logger.logger()[root@python3 week5]# python3 module/bin.pylogging in logger
两层目录,再调用模块中的一个方法
[root@python3 week5]# cat module/bin.py#!/usr/local/python3/bin/python3from web.web2.logger import logger ####logger()[root@python3 week5]# python3 module/bin.pylogging in logger
包调用的过程
[root@python3 week5]# cat module/web/__init__.pyprint('web init')[root@python3 week5]# cat module/bin.py #!/usr/local/python3/bin/python3import web #调用用包,实际是执行__init__.py的代码[root@python3 week5]# python3 module/bin.pyweb init
import模块与包的一个区别
模块调用:[root@python3 week5]# cat module/calculate.py#!/usr/local/python3/bin/python3x=9 #####def add(x,y): return x+ydef sub(x,y): return x-y[root@python3 week5]# cat module/bin.py #!/usr/local/python3/bin/python3import calculateprint(calculate.x) #####[root@python3 week5]# python3 module/bin.py9
包调用:
[root@python3 week5]# cat module/web/main.py#!/usr/local/python3/bin/python3x=4 ###[root@python3 week5]# cat module/bin.py #!/usr/local/python3/bin/python3import web #import一个包,只是执行__init__与其他的模块没关系print(web.main.x)[root@python3 week5]# python3 module/bin.pyweb initTraceback (most recent call last): File "module/bin.py", line 3, inprint(web.main.x) #####不能以这种方式调用包中的模块中的一个变量AttributeError: module 'web' has no attribute 'main'[root@python3 week5]# cat module/bin.py#!/usr/local/python3/bin/python3from web import main ##正常的方式print(main.x)[root@python3 week5]# python3 module/bin.pyweb init4
四、BASEDIR的问题
1、目录结构[root@python3 week5]# tree atm/atm/├── bin #程序入口,程序有一个执行文件│ ├── bin.py│ └── __init__.py├── conf #配置相关│ ├── __init__.py│ └── setting.py└── module #与逻辑相关 ├── __init__.py ├── logger.py └── main.py模块之间调用:[root@python3 week5]# cat atm/module/logger.py #!/usr/local/python3/bin/python3def logging(): print('module logging')[root@python3 week5]# cat atm/module/main.py #!/usr/local/python3/bin/python3import logger ####def main(): logger.logging() ####main()[root@python3 week5]# python3 atm/module/main.pymodule logging
2、调用分析
a.logger模块是打印日志,被main模块后调用,可以调用成功,但是使用bin来调用时,可以找到main,但是由于bin和logger不在同一目录下,所以bin调用时打不到logger[root@python3 atm]# cat module/logger.py #!/usr/local/python3/bin/python3def logging(): print('module logging')[root@python3 atm]# cat module/main.py #!/usr/local/python3/bin/python3import loggerdef main(): logger.logging()[root@python3 atm]# cat bin/bin.py #!/usr/local/python3/bin/python3from module import mainmain.main()[root@python3 bin]# python3 bin.py Traceback (most recent call last): File "bin.py", line 2, infrom module import mainModuleNotFoundError: No module named 'module' 找不到,但是在pycharm时,它会自动找追加路径到sys.path下,以避免了错误
b.要找到module就要实现自动追加脚本所有的目录路径
__file__:内部变量,取脚本名称__file__:内部变量,取脚本名称[root@python3 bin]# cat bin.py #!/usr/local/python3/bin/python3print(__file__)[root@python3 bin]# python3 bin.py bin.py
path.abspath取绝对路径
[root@python3 bin]# cat bin.py #!/usr/local/python3/bin/python3import osprint(__file__)print(os.path.abspath(__file__))[root@python3 bin]# python3 bin.py bin.py/root/py/fullstack_s2/week5/atm/bin/bin.py
使用os.path.dirname取得atm的路径
[root@python3 bin]# cat bin.py #!/usr/local/python3/bin/python3import osprint(__file__)print(os.path.abspath(__file__))print(os.path.dirname(os.path.abspath(__file__)))print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))[root@python3 bin]# python3 bin.py bin.py/root/py/fullstack_s2/week5/atm/bin/bin.py/root/py/fullstack_s2/week5/atm/bin/root/py/fullstack_s2/week5/atm
使用sys.path.append追加到模块查找到目录
[root@python3 bin]# cat bin.py #!/usr/local/python3/bin/python3import os,sysBASIC_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ###sys.path.append(BASIC_DIR) ###from module import mainmain.main()[root@python3 bin]# python3 bin.py ##再执行成功 module logging
c.整个程序结构中所需要目录
假设项目为foo,建议有以下的目录结构[root@python3 day2]# tree foo/foo/├── bin│ └── foo├── docs│ ├── abc.rst│ └── conf.py├── foo│ ├── __init__.py│ ├── main.py│ └── test │ ├── __init__.py│ └── test__main.py├── README├── requirements.txt└── setup.py
注释:
bin目录:存放项目的一些可执行文件,可以自定义为script/之类foo目录:存放项目的所有源代码 - 源代码的所有模块、包都应该放在此目录,不要置于顶层目录 - 其子目录test/存放单元测试代码 - 程序的入口最好命名为main.pydocs目录:存放一些文档setup.py: 安装、部署、打包的脚本(写了一个软件,这个软件要有这的一个执行环境,可以通过这个命令安装好)requirements.txt:存放软件依赖的外部Python包列表README: 项目说明文件 README:简要描述该项目信息,让读者快速了解这个项目说明事项: a.软件定位,软件的基本功能(开发语言的版本,如有些就使用Python2.7上运行) b.运行代码的方法,安装环境、启动命令等 c.简要使用说明(如ftp有8个功能,那些已经实现,那些没实现要明说) d.代码目录结构说明,量多详细点可以说明软件的基本信息 e.常见问题说明
requirements.txt和setup.py
setup.py描述:使用setup.py来管理代码的打包、安装、部署问题.业界标准写法是用Python流行的打包工具setuptools来管理, 这种方式普遍用于开源项目中,不这这里的核心思想不是用标准化的工具来解决这些问题,而是说,一个项目一定 要有一个安装部署工具,能快速便捷的在一台新机器上将环境安装好、代码都部署好和将程序运行起来注意事项: - 安装环境时经常忘记最近又添加一个新的python包,结果一到线运行,程序就会出错 - Python包的版本依赖问题,有时程序中使用是一个版本的Python包,但是官方的已经是最新的包了,通过手动安装可能出错 - 如果依赖包很多,一个个安装这些依赖很费时 - 新手写项目时,将程序运行起来非常麻烦,因为可能经常忘记要怎么安装各种依赖 所以setup.py可以将这些事情自动化起来,提高效率、减少出错的概率
requirements.txt
这个文件存在的目的是: - 方便开发者维护软件的包依赖,将开发过程中新增的包添加进这个列表中,避免在setup.py安装依赖时漏掉软件包 - 方便读者明确项使用了哪些Python包这个文件的格式是每一行包含一个包依赖的说明,通常flask>=0.10这种格式,要求是这个格式能被pip识别,这样就可以简单的通过pip install -r requirements.txt来把所有Python包依赖都装好了https://pip.pypa.io/en/stable/user_guide/
配置文件的使用方法
注意:在上面的目录结构中,没有将conf.py放在源代码目录下,而是放在docs/的目录下很多项目对配置文件的使用是 - 配置文件写在一个或多个python文件,如这里的conf.py - 项目中那个模块用到这个配置文件就直接通过import conf这种形式来做代码中使用配置产生的问题 - 让单元测试变得困难(因为模块内部依赖了外部配置) - 另一方面配置文件作为用户控制程序的接口,应当可以由用户自由指定该文件的路径 - 程序组组件可复用性太差,因为这种惯穿所有模块的代码编码方式,使得大部分模块依赖conf.py这个文件更好的配置方式 - 模块的配置都是可以灵活配置的,不受外部配置文件的影响 - 程序配置也是可以灵活控制的(如nginx,mysql的配置文件)所以,不应当在代码中直接import conf来使用配置文件,上面目录结构的conf.py,是给出一个配置样例,不是在写死的程序中直接引用的配置文件,可以通过给main启动配置路径的方式来让程序读取配置内容,当然,这里的conf.py可以换个类似的名字,比如setting.py.或者可以使用其他格式的内容来编写配置文件,如setting.yaml之类的.
五、__name__变量
分析:if __name__=='__main__': 把程序写好,如何知道那些功能模块,配置等,一般模块之间是调用,本身是不希望调用的,当别人在调用时,会把测试代码也会被一起调用
[root@python3 day2]# cat name/foo.py #!/usr/local/python3/bin/python3def hi(): print('hi')hi() #一般功能模块只用来调用,不需要执行,但是有时写完会做测试[root@python3 day2]# cat name/bin.py#!/usr/local/python3/bin/python3import foofoo.hi()[root@python3 day2]# python3 name/bin.pyhi #import foo时会加载一次所有代码hi
解决方法:使用if __name__=='__main__'
[root@python3 day2]# cat name/foo.py#!/usr/local/python3/bin/python3def hi(): print('hi')if __name__=='__main__': hi() #可以作测试,而且在其他人使用时,测试的不会显示[root@python3 day2]# python3 name/foo.pyhi[root@python3 day2]# cat name/bin.py #!/usr/local/python3/bin/python3import foo#foo.hi()[root@python3 day2]# python3 name/bin.py
__name__的原理
[root@python3 day2]# cat name/foo.py #!/usr/local/python3/bin/python3def hi(): print('hi')print(__name__)[root@python3 day2]# python3 name/foo.py__main__ #本身使用时是__main__[root@python3 day2]# cat name/bin.py #!/usr/local/python3/bin/python3import foo[root@python3 day2]# python3 name/bin.pyfoo
六、模拟实现一个ATM + 购物商场程序
- 额度15000或自定义- 实现购物商场,买东西加入购物车,调用信用卡接口结账(要输入卡号和密码之类的)- 可以提现,手续费5%- 每月22号出账单,每月10号为还款日,过期未还,按欠款总额万分之5每日计息 - 支持多账户登录(多个用户都可以登录购物)- 支持账户间转账(一个用户转到另一个用户,一个用户减,另一个用户就加上相应的款)- 记录每月日常消费流水(在购物时加上)- 提供还款接口- atm记录操作日志(转账、还款之类的,是银行操作的)- 提供管理接口,包括添加账户、用户额度、冻结账户等- 用户认证用装饰器