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记录操作日志(转账、还款之类的,是银行操作的)
- 提供管理接口,包括添加账户、用户额度、冻结账户等
- 用户认证用装饰器

 

posted @ 2018-04-26 09:16  Reid21  阅读(171)  评论(0编辑  收藏  举报