Python攻克之路-module
一、模块
例子:调用configpaser模块时,首先在当前的Python编译器去查找,找到后,里面没有ConfigParser()这个方法,直接报错,configparser应该让它去系统查找
import configparser config = 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/python3
def add(x,y):
return x+y
def sub(x,y):
return x-y
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
import calculate ##调用
print(calculate.add(1,2))
[root@python3 module]# python3 bin.py
3
3、模块的搜索路径
[root@python3 module]# cat bin.py #!/usr/local/python3/bin/python3 import sys ##调用sys模块查看 import calculate print(calculate.add(1,2)) print(sys.path) ##sys.path方法 [root@python3 module]# [root@python3 module]# python3 bin.py #第一个路径就是当前的目录下module 3 ['/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/python3
print('cal is ok') ##打印
def add(x,y):
return x+y
def sub(x,y):
return x-y
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
import sys
import calculate ##只是调用
[root@python3 module]# python3 bin.py
cal is ok
变量的调用也是要通过calculate调用
[root@python3 module]# cat calculate.py
#!/usr/local/python3/bin/python3
print('cal is ok')
x = 4 ##
def add(x,y):
return x+y
def sub(x,y):
return x-y
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
import sys
import calculate
print(x) ###直接调用x
[root@python3 module]# python3 bin.py
cal is ok
Traceback (most recent call last):
File "bin.py", line 4, in <module>
print(x)
NameError: name 'x' is not defined ##未定义
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
import sys
import calculate
print(calculate.x) ##通过calculate的方法调用正常
[root@python3 module]# python3 bin.py
cal is ok
4
import多个模块
import time,sys
2.从模块中调用某个方法(from module import func)
场景:模块文件里有很多函数,通过import相当于把所有的都加载一次,可以只调用某个函数来提高使用效率
[root@python3 module]# cat calculate.py
#!/usr/local/python3/bin/python3
print('cal is ok')
x = 4
def add(x,y):
return x+y
def sub(x,y):
return x-y
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
from calculate import add #从calculate中调用add
print(add(2,3)) #直接使用函数
[root@python3 module]# python3 bin.py
cal is ok
5
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
from calculate import add,sub ##同时调用多个
print(add(2,3))
print(sub(9,3))
[root@python3 module]# python3 bin.py
cal is ok
5
6
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
from calculate import add,sub #通过from指定调用时,没有调用x,会报错,表示python并没加载x
print(add(2,3))
print(sub(9,3))
print(x)
[root@python3 module]# python3 bin.py
cal is ok
5
6
Traceback (most recent call last):
File "bin.py", line 5, in <module>
print(x)
NameError: name 'x' is not defined ###
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
from calculate import add,sub
print(add(2,3))
print(sub(9,3))
print(calculate.x) ##通过calculate去调用,但是还是报错,calculate没被定义,证明只是加载了add,sub
[root@python3 module]# python3 bin.py
cal is ok
5
6
Traceback (most recent call last):
File "bin.py", line 5, in <module>
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 ok 13 4 -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/python3
print('cal is ok')
x = 4
def add(x,y):
return x+y
def sub(x,y):
return x-y
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
from calculate import *
def add(x,y):
return x+y+9
print(add(4,5))
[root@python3 module]# python3 bin.py
cal is ok
18
类似同一个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/python3
print('cal is ok') ##
print('cal2 is ok') ##
x = 4
def add(x,y):
return x+y
def sub(x,y):
return x-y
[root@python3 module]# cat bin.py
#!/usr/local/python3/bin/python3
from 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/python3 from calculate import add as plus print(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/python3
def logger():
print('logging in logger')
[root@python3 week5]# cat module/bin.py
#!/usr/local/python3/bin/python3
from web import logger ##from package import module
logger.logger()
[root@python3 week5]# python3 module/bin.py
logging 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/python3
from web.web2 import logger ##使用点
logger.logger()
[root@python3 week5]# python3 module/bin.py
logging in logger
两层目录,再调用模块中的一个方法
[root@python3 week5]# cat module/bin.py #!/usr/local/python3/bin/python3 from web.web2.logger import logger #### logger() [root@python3 week5]# python3 module/bin.py logging in logger
包调用的过程
[root@python3 week5]# cat module/web/__init__.py
print('web init')
[root@python3 week5]# cat module/bin.py
#!/usr/local/python3/bin/python3
import web #调用用包,实际是执行__init__.py的代码
[root@python3 week5]# python3 module/bin.py
web init
import模块与包的一个区别
模块调用:
[root@python3 week5]# cat module/calculate.py
#!/usr/local/python3/bin/python3
x=9 #####
def add(x,y):
return x+y
def sub(x,y):
return x-y
[root@python3 week5]# cat module/bin.py
#!/usr/local/python3/bin/python3
import calculate
print(calculate.x) #####
[root@python3 week5]# python3 module/bin.py
9
包调用:
[root@python3 week5]# cat module/web/main.py
#!/usr/local/python3/bin/python3
x=4 ###
[root@python3 week5]# cat module/bin.py
#!/usr/local/python3/bin/python3
import web #import一个包,只是执行__init__与其他的模块没关系
print(web.main.x)
[root@python3 week5]# python3 module/bin.py
web init
Traceback (most recent call last):
File "module/bin.py", line 3, in <module>
print(web.main.x) #####不能以这种方式调用包中的模块中的一个变量
AttributeError: module 'web' has no attribute 'main'
[root@python3 week5]# cat module/bin.py
#!/usr/local/python3/bin/python3
from web import main ##正常的方式
print(main.x)
[root@python3 week5]# python3 module/bin.py
web init
4
四、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/python3
def logging():
print('module logging')
[root@python3 week5]# cat atm/module/main.py
#!/usr/local/python3/bin/python3
import logger ####
def main():
logger.logging() ####
main()
[root@python3 week5]# python3 atm/module/main.py
module logging
2、调用分析
a.logger模块是打印日志,被main模块后调用,可以调用成功,但是使用bin来调用时,可以找到main,但是由于bin和logger不在同一目录下,所以bin调用时打不到logger
[root@python3 atm]# cat module/logger.py
#!/usr/local/python3/bin/python3
def logging():
print('module logging')
[root@python3 atm]# cat module/main.py
#!/usr/local/python3/bin/python3
import logger
def main():
logger.logging()
[root@python3 atm]# cat bin/bin.py
#!/usr/local/python3/bin/python3
from module import main
main.main()
[root@python3 bin]# python3 bin.py
Traceback (most recent call last):
File "bin.py", line 2, in <module>
from module import main
ModuleNotFoundError: No module named 'module' 找不到,但是在pycharm时,它会自动找追加路径到sys.path下,以避免了错误
b.要找到module就要实现自动追加脚本所有的目录路径
__file__:内部变量,取脚本名称
__file__:内部变量,取脚本名称 [root@python3 bin]# cat bin.py #!/usr/local/python3/bin/python3 print(__file__) [root@python3 bin]# python3 bin.py bin.py
path.abspath取绝对路径
[root@python3 bin]# cat bin.py #!/usr/local/python3/bin/python3 import os print(__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/python3 import os print(__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/python3 import os,sys BASIC_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ### sys.path.append(BASIC_DIR) ### from module import main main.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.py
docs目录:存放一些文档
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/python3
def hi():
print('hi')
hi() #一般功能模块只用来调用,不需要执行,但是有时写完会做测试
[root@python3 day2]# cat name/bin.py
#!/usr/local/python3/bin/python3
import foo
foo.hi()
[root@python3 day2]# python3 name/bin.py
hi #import foo时会加载一次所有代码
hi
解决方法:使用if __name__=='__main__'
[root@python3 day2]# cat name/foo.py
#!/usr/local/python3/bin/python3
def hi():
print('hi')
if __name__=='__main__':
hi() #可以作测试,而且在其他人使用时,测试的不会显示
[root@python3 day2]# python3 name/foo.py
hi
[root@python3 day2]# cat name/bin.py
#!/usr/local/python3/bin/python3
import foo
#foo.hi()
[root@python3 day2]# python3 name/bin.py
__name__的原理
[root@python3 day2]# cat name/foo.py
#!/usr/local/python3/bin/python3
def 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/python3
import foo
[root@python3 day2]# python3 name/bin.py
foo
六、模拟实现一个ATM + 购物商场程序
- 额度15000或自定义
- 实现购物商场,买东西加入购物车,调用信用卡接口结账(要输入卡号和密码之类的)
- 可以提现,手续费5%
- 每月22号出账单,每月10号为还款日,过期未还,按欠款总额万分之5每日计息
- 支持多账户登录(多个用户都可以登录购物)
- 支持账户间转账(一个用户转到另一个用户,一个用户减,另一个用户就加上相应的款)
- 记录每月日常消费流水(在购物时加上)
- 提供还款接口
- atm记录操作日志(转账、还款之类的,是银行操作的)
- 提供管理接口,包括添加账户、用户额度、冻结账户等
- 用户认证用装饰器


浙公网安备 33010602011771号