9.python模块化-经常使用的python标准库模块

2019年11月19日 阅读数:41
这篇文章主要向大家介绍9.python模块化-经常使用的python标准库模块,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

模块化
一.模块
1.模块module:为了编写可维护的代码,咱们把不少函数分组,分别放到不一样的文件里,这样,每一个文件包含的代码就相对较少,不少编程语言都采用这种组织代码的方式。在python中,一个.py文件就称之为一个模块(Module)
2.模块的好处:
(1)提升了代码的可维护性
(2)编写代码没必要从零开始。当一个模块编写完毕,就能够被其余地方引用。
3.模块一共分三种:
(1)python标准库(python自带的模块)
(2)第三方模块(须要安装)
(3)应用程序自定义模块
二.导入语句
1.导入语句的三种方式(import语句--from语句--自定义语句)
方式一:import语句
(1)找到指定的模块,加载和初始化它,生成模块对象。找不到,抛出ImportError异常
(2)在import所在的做用域的局部命名空间中,增长名称和上一步建立的对象关联node

      语句                   含义
import 模块1,[,模块2,...]    彻底导入
import ... as ...          模块别名

例子1:python

#导入functools模块到本地做用域
import functools

#打印当前做用域模块属性包含导入的functools模块
print(dir())                                #打印结果:[...,'functools']

#收集functools模块的全部属性
print(dir(functools))                        #打印结果:[...,'wraps']

#打印functools这个模块对象,经过functools名称放到当前做用域内
print(functools)                             #打印结果:<module 'functools' from 'E:\\python3\\lib\\functools.py'>

#经过functools这个模块对象访问它其中的资源wraps
print(functools.wraps)                       #打印结果:<function wraps at >

例子2:mysql

#导入os模块到本地做用域
import os.path

#打印当前做用域模块属性包含导入的os模块不包含path
print(dir())                           #打印结果:[....,'os']

#收集os模块的全部属性
print(dir(os))                        #打印结果:[...,'path']

#打印os这个模块对象,经过os名称放到当前做用域内
print(os)                             #打印结果:<module 'os' from 'E:\\python3\\lib\\os.py'>

#经过os这个模块对象访问它其中的资源path(彻底限定名称访问path)
print(os.path)                        #打印结果:<module 'ntpath' from 'E:\\python3\\lib\\ntpath.py'>

例子3:linux

#导入os.path并赋值给osp到本地做用域
import os.path as osp

#打印当前做用域模块属性包含导入的ops(os.path模块对象)
print(dir())                           #打印结果:[....,'osp']

#打印ops(os.path)这个模块对象
print(osp)                             #打印结果:<module 'ntpath' from 'E:\\python3\\lib\\ntpath.py'>

总结:
①导入顶级模块,其名称会加入到本地名词空间中,并绑定到其模块对象
②导入非顶级模块,将其顶级模块加入到本地名次空间中。导入的模块必须使用彻底限定名来访问
③若是使用了as,其后的名称直接加入到本地名次空间中,并直接绑定到导入的模块对象
方式二:from语句程序员

        语句                   含义
from ... import ...           部分导入
from ... improt ... as ...    别名

例子1:web

#加载初始化pathlib模块下的指定成员Path和PosixPath加入本地名次空间并绑定
from pathlib import Path,PosixPath

#打印当前做用域模块属性
print(dir())           #打印结果:['Path', 'PosixPath', ....]

#打印pathlib模块下的模块属性Path
print(Path)            #打印结果:<class 'pathlib.Path'>

#打印pathlib模块下的模块属性PosixPath
print(PosixPath)       #打印结果:<class 'pathlib.PosixPath'>

例子2:正则表达式

#加载初始化pathlib模块下全部公共成员
from pathlib import *

#打印当前做用域模块属性
print(dir())           #打印结果:['Path', 'PosixPath', 'PurePath', 'PurePosixPath', 'PureWindowsPath', 'WindowsPath', .....]

#打印pathlib模块下的其中一个模块属性Path
print(Path)            #打印结果:<class 'pathlib.Path'>

#打印pathlib模块下的其中一个模块属性PosixPath
print(PosixPath)       #打印结果:<class 'pathlib.PosixPath'>

例子3:算法

#导入pathlib模块下的指定成员wraps取别名为wr
from functools import wraps as wr

#打印当前做用域模块属性
print(dir())           #打印结果:[...', 'wr']

#打印pathlib模块下的属性对象wr(wraps)
print(wr)             #打印结果:<function wraps at 0x0000000000D99BF8>

例子4:sql

#加载初始化os.path模块,exists加入本地名次空间并绑定
from os.path import exists
#打印当前做用域模块属性(只有exists没有os和os.path)
print(dir())           #打印结果:['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'exists']
#打印os.path模块模块下的属性对象exists
print(exists)          #打印结果:<function wraps at 0x0000000000D99BF8>

#导入os模块到本地做用域
import os
#打印当前做用域模块属性包含导入的os模块
print(dir())           #打印结果:['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'exists', 'os']
#经过os模块找到path.exists
print(os.path.exists)  #打印结果:<function wraps at 0x0000000000D99BF8>

总结:
①找到from子句中指定的模块,加载并初始化它(注意不是导入)
②对于import子句后的名称
-先查from子句导入的模块是否居右该名称的属性
-若是不是,则常识导入该名称的子模块
-尚未找到,则抛出ImportError异常
-这个名称保存到本地名词空间中,若是有as子句,则使用as子句后的名称
方式三:自定义模块:py文件就是一个模块
举例1:
<1>test1.py模块里shell

x = 123

<2>test2.py

#导入test1文件
import test1

#打印当前做用域模块属性包含导入的test1
print(dir())           #打印结果:['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test1']
#调用test1模块里的x
print(test1.x)         #打印结果:123

举例2:
<1>test1.py文件

x = 123

<2>test2.py加载初始化test1模块,x加入本地名次空间并绑定

from test1 import x

#打印当前做用域模块属性包含导入的x
print(dir())           #打印结果:['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'x']
#调用test1模块里的x
print(x)               #打印结果:123

举例3:
<1>test1.py文件

print('this is test1 module')     #第二步:经过导入模块找到test1文件打印:this is test1 module

class A:
    def show(self):
        print(type(self).__name__)

a = A()
#第三步:test1模块内部执行了实例a调用show方法
a.show()                              #打印:A

#第四步:打印当前做用域模块属性包含
print(dir())                          #打印结果:['A', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a']

#第五步:打印test1的名字
print(__name__)                       #打印结果:test1

<2>test2.py文件

print('this is test2 module')   #第一步:打印:this is test2 module

#导入test1文件初始化test1.py本身运行
import test1

#第六步:打印当前做用域模块属性包含导入的test1
print(dir())    #打印结果:['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test1']

#用test1的类构建出一个新的实例,这个实例放在当前模块属性里
b = test1.A()

#第七步:打印当前做用域模块属性包含b实例
print(dir())     #打印结果:['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'b', 'test1']

#第八步:用实例b调用show方法
b.show()         #打印结果:A

#第九步:能够调用到test1里的名词空间a
c = test1.a
print(c)         #打印结果:<test1.A object at 0x0000000000AD2978>

#第十步:查看test2的名字由于是入口叫__main__
print(__name__)  #打印结果:__main__

总结:自定义模块命名规范
①模块名就是文件名
②模块名必须符合标识符的要求,非数字开头的字母数字和下划线的组合。test-module.py这样的文件名不能做为模块名
③不要使用系统模块名来避免冲突,除非你明确知道这个模块名的用途
④一般模块名为全小写,下划线来分割
2.导入语句问题
from json import encoder以后,json.dump函数用不了?为何
import json.encoder以后呢?json.dump函数能用吗
缘由是from json import encoder以后,当前名次空间没有json,可是json模块已经加载过了,没有json的引用,没法使用dump函数
import json.encoder也加载json模块,可是当前名次空间有json,所以能够调用。
三.模块的属性与运行
1.模块的属性

   属性                 含义
__file__         字符串,源文件路径
__cached__       字符串,编译后的字节码文件路径
__spec__         显示模块的规范
__name__         模块名
__package__      当模块的包,同__name__,不然,能够设置为顶级模块的空字符串

2.模块运行
(1)__name__,每一个模块都会定一个__name__特殊变量来存储当前模块的名称,若是不指定,则默认为源代码文件名词,若是是包则有限定名。
(2)解释器初始化的时候,会初始化sys.modules字典(保存已加载的模块),建立builtins(全局函数,常量)模块,__main__模块,sys模块,以及模块搜索路径sys.path
(3)python是脚本语言,任何一个脚本均可以直接执行,也能够做为模块被导入:
①当从标准输入(命令行方式敲代码),脚本($python test.py)或交互式读取的时候,会将模块的__name__设置为__main__,模块的顶层代码就在__main__这个做用域执行。顶层代码:模块中缩进最外层的代码。

import sys

#初始化sys.modules字典打印__main__
print(sys.modules['__main__'])          #打印结果:<module '__main__' from 'E:/wangshiyao/aaa/xi/test1.py'>

#打印当前模块(顶层模块)名,将模块的__name__设置为__main__
print(__name__)                         #打印结果:__main__

②若是是import导入的,其__name__默认就是模块名
<1>test1.py文件

import sys
#导入teset2文件
import test2
#打印当前模块(顶层模块)名,将模块的__name__设置为__main__
print(__name__)                 #打印结果:__main__

<2>test2.py文件

print('this is test2 module')   #打印结果:this is test2 module
print("~~~~~~~~~~~",__name__)   #打印结果:~~~~~~~~~~~ test2
####打印结果:
this is test2 module
~~~~~~~~~~~ test2
__main__

总结:当运行test1.py文件导入test2.py模块的时候默认名就是模块的名字
(4)if __name__=="__main__": 用途:
①本模块的功能测试:
测试本模块内的函数,类
②避免主模块变动的副做:
用顶层代码,没有封装,主模块使用没有问题。可是,一单有了新的主模块,当前模块要被导入,因为原来代码没有封装,一并执行了
<1>test1.py文件

#主模块
import sys

#判断是不是主模块
if __name__ == "__main__":
    #若是是主模块执行如下代码
    print('我是主模块')
    #导入teset2文件
    import test2
else :
    #若是不是主模块执行如下代码
    print('in module {}'.format(__name__))

<2>test2.py文件

#不是主模块
print('this is test2 module')   #打印:this is test2 module

#判断是不是主模块
if __name__ == "__main__":
    #若是是主模块执行如下代码
    #测试代码:
    print('本身调用测试代码')
else :
    #若是不是主模块执行如下代码
    #主模块调用到
    print('in module {}'.format(__name__))
###当执行test2.py返回 this is test2 module 本身调用测试代码 ###当执行test1.py返回 我是主模块 this is test2 module in module test2

四.包
1.包(package)
(1)Python中只有一种模块对象类型,可是为了模块化组织模块的便利,提供一个概念---包Python中只有一种模块对象类型,可是为了模块化组织模块的便利,提供一个概念---包
(2)包指的是模块组织在一块儿的和包名同名的目录及其相关文件
(3)python的模块能够按照目录组织为包,引入了包之后,只要顶层的包名不与别人冲突,那全部模块都不会与别人冲突
(4)ptyhon中,目录能够做为模块,这就是包,不过代码须要写在该目录下__init__.py中
举例:
<1>m目录包含:__init__.py文件

y = 123

<2>m目录包含:x.py文件
<3>m目录上层目录test.py文件:

import m.x
#打印当前做用域包含调用的模块m
print(dir())                 #打印结果:['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']

#打印这个目录究竟是什么?
print(type(m))              #打印结果:<class 'module'>

#查看m模块包含x.py和y
print(dir(m))                #打印结果:['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'x', 'y']

#m是模块能把m目录里的 x.py文件归到我管
print(m.x)                  #打印结果:<module 'm.x' from 'E:\\shishi\\aaa\\m\\x.py'>

#m模块能够借助__init__.py调用到里面的东西
print(m.y)                  #打印结果:123

#编译后的字节码文件路径
print(m.__cached__)        #打印结果:E:\shishi\aaa\m\__pycache__\__init__.cpython-35.pyc

#打印源文件路径
print(m.__file__)          #打印结果:E:\shishi\aaa\m\__init__.py

 

 

包目录下的py文件,子目录都是其子模块:如图

如上创建子模块目录和文件,全部py文件中就写一句话print(__name__)
<1>test.py

from m.m1.m11 import *

<2>\m\__init__.py

print(__name__)

<3>\m\m1\__init__.py

print(__name__)

<4>\m\m1\m11\__init__.py

print(__name__)

###打印结果:
m
m.m1
m.m1.m11
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm11prop']

3.模块和包的总结
(1)包可以更好的组织模块,尤为是大模块代码不少,能够拆分红不少子模块,便于使用某些功能就加载相应的子模块。
(2)包目录__init__.py是在包第一次导入的时候就会执行,内容能够为空,也能够是用于该包初始化工做的代码,最好不要删除它(低版本不可删除)
(3)导入子模块必定会加载父模块,可是导入父模块必定不会导入子模块
(4)包目录之间只能使用.点号做为间隔符,表示模块及其子模块的层级关系
(5)模块也就是封装,如同类,函数,不过它可以封装变量,类,函数。
(6)模块就是命名空间,其内部的顶层标识符,都是它的属性,能够经过__dict__或dir(module)查看
(7)包也是模块,但模块不必定是包,包是特殊的模块,是一种组织方式,它包含__path__属性
五.绝对导入和相对导入
1.绝对导入
在import语句或者from导入模块,模块名称最前面不是以.点开头的
绝对导入老是去搜索模块搜索路径中找
2.相对导入
只能在包内使用,且只能用在from语句中
使用.点号,表示单曲目录内
..表示上一级目录
不要在顶层模块中使用相对导入
举例:
<1>一级目录m包括二级目录m1和本身的__init__.py文件,还包括mm.py文件,mmm.py文件,mmmm.py文件
①__init__.py文件

print(__name__)
#调用本身目录下的mm
from . import mm

②mm.py文件

print('我是m目录下的文件mm.py,我被m目录下的__init.py导入')

③mmm.py文件

print('我是m目录下的文件mmm.py,我被m目录下的m1目录的__init.py导入')

④mmmm.py文件

print('我是m目录下的文件mmmm.py,我被m目录下的m1目录下的m11目录的__init.py导入')

<2>二级目录m1包括三级目录m11和本身的__init__.py文件

print(__name__)
#调用上一层目录m目录下的mm
from .. import mmm

<3>三级目录m11包括本身的__init__.py文件

print(__name__)
#调用上一层目录的上一层目录m目录下的mmm
from ... import mmmm

<4>一级目录m同级有一个调用文件a.py

#调用m,m1,m11模块
from m.m1.m11 import *
####返回结果:
m
我是m目录下的文件mm.py,我被m目录下的__init.py导入
m.m1
我是m目录下的文件mmm.py,我被m目录下的m1目录的__init.py导入
m.m1.m11
我是m目录下的文件mmmm.py,我被m目录下的m1目录下的m11目录的__init.py导入

六.下划线开头的模块名
1.模块内的标识符
普通变量,保护变量,私有变量,特殊变量,都没有被隐藏,也就是说模块内没有私有变量,在模块中定义不作特殊处理
2.三种语句调用方式的不一样
方式一:import语句
<1>一级目录m包括本身的__init__.py文件
①__init__.py文件

_x = 'x'
__y = 'y'

<2>一级目录m同级有一个调用文件a.py

#导入m模块到本地做用域
import m

#打印当前做用域模块属性包含导入的m模块
print(dir())

#能够经过m模块调用到里面的_x
print(m._x)

#能够经过m模块调用到里面的__y
print(m.__y)
####返回结果:
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
x
y

方式二:from语句
<1>一级目录m包括本身的__init__.py文件
①__init__.py文件

_x = 'x'
__y = 'y'

<2>一级目录m同级有一个调用文件a.py

#加载初始化m模块,_x和__y加入本地名次空间并绑定
from m import _x,__y

#打印当前做用域模块属性包含导入的_x,__y
print(dir())

#能够经过m模块调用到里面的_x
print(_x)

#能够经过m模块调用到里面的__y
print(__y)
#####打印结果:
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__y', '_x']
x
y

总结:依然可使用from语句,访问全部变量
方式三:from ...import *和__all__
(1)__all__是一个列表,元素是字符串,每个元素都是一个模块内的变量名
<1>一级目录m包括本身的__init__.py文件
①__init__.py文件

#加上__all__可让下划线开头的被*导入
__all__ = ['_x','__y']
_x = 'x'
__y = 'y'

<2>一级目录m同级有一个调用文件a.py

#加载初始化m模块下全部公共成员(不包括下划线开头的)
from m import *

#打印当前做用域模块属性包含导入的_x,__y
print(dir())

#能够经过m模块调用到里面的_x
print(_x)

#能够经过m模块调用到里面的__y
print(__y)
#####打印结果:
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__y', '_x']
x
y

总结:使用from m import *能够导入__all__列表中的名称
七.包和子模块
1.包和子模块调用方式
<1>一级目录m包括本身的__init__.py文件,还包括m1.py文件
①__init__.py文件

print(__name__)
x = 5

②m1.py文件

print(__name__)
y  =6

<2>一级目录m同级有一个调用文件a.py
如何访问到m1.py中的变量m1?
方式一:a.py

#导入m模块和m模块里的m1
import m.m1
#打印当前做用域模块m
print(dir())
#经过m1访问到里面的y
print(m.m1.y)

###打印结果:
m
m.m1
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
6

方式二:

#直接导入m.m1的属性y
from m.m1 import y
#打印当前做用域模块属性包含y
print(dir())

print(y)
###打印结果:
m
m.m1
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'y']
6

方式三:
须要在__init__.py增长:__all__ = ['x','m1'],使用__all__提供导出的名称

#加载初始化m模块下全部公共成员(不包括下划线开头的)
from m import *

#打印当前做用域模块属性
print(dir())

print(m1.y)
###打印结果:
m
m.m1
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm1', 'x']
6

方式四:不用__all__使用相对导入
须要在__init__.py增长:from . import m1  #当前目录下的m1模块

#加载初始化m模块下全部公共成员(不包括下划线开头的)
from m import *

#打印当前做用域模块属性
print(dir())

print(m1.y)
###打印结果:
m
m.m1
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm1', 'x']
6

2.包和子模块调用方式总结
(1)使用from m import * 导入
①若是模块没有__all__,from m import *只导入非下划线开头的模块的变量。若是是包,子模块也不会导入,除非在__all__中设置,或__init__.py中使用相对导入
②若是模块有__all__,from m import * 只导入__all__列表中指定的名称,哪怕这个名次是下划线开头的,或者是子模块
③from m import (方式导入,使用简单,可是其反作用是导入大量不须要使用的变量,甚至有可能形成名称冲突。而__all__能够控制被导入模块在这种导入方式下可以提供的变量名称,就是为了阻止from m import *导入过多的模块变量,从而避免冲突。所以,编写模块时,应该尽可能加入__all__
(2)from module import name,name2导入
①这种方式的导入是明确的,哪怕是导入子模块,或者导入下划线开头的名称,程序员能够有控制的导入名称和其对应的对象
八.模块变量修改
<1>一级目录m包括本身的__init__.py文件,还包括m1.py文件
①__init__.py文件

print(__name__)
x = 5

②m1.py文件

print(__name__)
y  =6

<2>一级目录m同级有一个调用文件a.py
a.py文件修改m1.py模块里的y
#导入m模块和m模块里的m1

from m import m1

#打印当前做用域模块属性
print(dir())

#打印y
print(m1.y)
#修改y
m1.y = 100
#再次打印y
print(m1.y)
######打印结果:
m
m.m1
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm1']
6
100

总结:
模块对象是同一个,所以模块的变量也是同一个,对模块变量的修改,会影响全部使用者。除非万不得已,或明确知道本身在作什么,不然不要修改模块的变量(能够经过猴子补丁动态修改模块)
九.包管理
1.为何使用包管理
(1)python的模块或者源文件直接能够复制到目标项目目录中,就能够导入使用了。可是为了更多项目调用,或者共享给别人,就须要打包,或发布到网络,以便供人使用。目的也是为了复用。
(2)Pypi(Python Package Index),公共的模块存储中心
2.主要工具
(1)distutils:官方distutils,使用安装脚本setup.py来构建,安装包
(2)setuptools:它是替代distutils的加强版工具集,包含easy_install工具,使用ez_setup.py文件。支持egg格式的构建和安装提供查询,下载,安装,构建,发布,管理等包管理功能,setuptools是包管理的核心模块
(3)pip:pip目前包管理的事实标准。构建在setuptools之上,替代easy_install的。一样提供丰富的包管理功能。
(4)wheel:提供bdist_wheel做为setuptools的扩展命令,这个命令能够用来生成wheel打包格式。pip提供了一个wheel子命令来安装wheel包。固然,须要先安装wheel模块。它可让Python库以二进制形式安装,而不须要在本地编译
3.使用setup.py打包:setup.py建立一个源代码分发包
(1)查看命令:python setup.py --help-commands
(2)查看命令的帮助命令:python setup.py --help
(3)build命令,编译
<1>一级目录m包括二级目录m2和本身的__init__.py文件,二级目录m2包括m21目录和本身的__init__.py,三级目录m2包括本身的__init__.py
<2>建立一个build目录命令:Python setup.py build
①如下是packages=['m']配置结果

E:\shishi\aaa>python setup.py build
running build
running build_py
creating build
creating build\lib
creating build\lib\m
copying m\m1.py -> build\lib\m
copying m\__init__.py -> build\lib\m

总结:在项目目录下多了build目录,有一个lib子目录,lib下就是模块m的目录了。m目录下的*.py文件被复制了,可是子目录没有被复制
②如下packages=['m.m2.m21']配置的结果

E:\shishi\aaa>python setup.py build
running build
running build_py
creating build\lib
creating build\lib\m
creating build\lib\m\m2
creating build\lib\m\m2\m21
copying m\m2\m21\__init__.py -> build\lib\m\m2\m21

总结:能够看出,逐级构建了童颜的目录结构,并只拷贝了m21的__init__.py文件
③如下packages=['m','m.m2','m.m2.m21']配置结果

E:\shishi\aaa>python setup.py build
running build
running build_py
creating build\lib
creating build\lib\m
copying m\__init__.py -> build\lib\m
creating build\lib\m\m2
copying m\m2\__init__.py -> build\lib\m\m2
creating build\lib\m\m2\m21
copying m\m2\m21\__init__.py -> build\lib\m\m2\m21

总结:build获得的文件,直接拷贝到其余项目就能够用
(5)install命令,安装
build后就能够install,直接运行命令:python setup.py install
就把当前打的包安装到当前python3环境里(E:\python3\Lib\site-packages)
若是没有build,会先build编译,而后安装
(6)sdist命令,分发
<1>sdist命令:建立源代码的分发包
<2>产生一个dist目录,里面生成一个带版本号的压缩包。
<3>在其余地方解压这个文件,里面有setup.py,就可使用python setup.py install 安装了,也能够pip install m-0.1.0zip直接使用ipi安装这个压缩包
①制做windows下的分发包命令:python setup.py sdist_wininst
获得zip包:E:\shishi\aaa\dist\m-0.0.1.zip包
②制做python setup.py bdist_rpm
能够把本身写好的模块发布到公共的Pypi上,也能够搭建Pypi私服,供企业内部使用,Pypi里面的模块没有太好的审核机制,不保证安全,请慎重使用。

十.经过模块实现插件化开发
动态导入:运行时,根据用户需求(提供字符串),找到模块的资源动态加载起来。
1.内建函数__import__()
__import__(name,globals=None,locals=None,fromlist=(),level=0)
name:模块名
import语句本质上就是调用这个函数。可是不鼓励直接使用它。建议使用importlib.import_module()
<1>test函数

class A:
    def showme(self):
        print('test模块下的A类')

<2>插件函数

#插件函数接
def plugin_load():

    #使用__import__是字符串方式导入模块赋值给mod(等价于import test)
    mod = __import__('test')

    #打印这个模块对象
    #print(mod)                         #打印结果:<module 'test' from 'E:\\shishi\\aaa\\m\\test.py'>

    #模块对象mod.A拿属性
    #print(mod.A)                       #打印结果:<class 'test.A'>

    #实例化A类
    getattr(mod, 'A')().showme()       #打印结果:test模块下的A类

if __name__ == '__main__':
    #须要的时候动态加载
    plugin_load()

2.内建函数importlib.import_module()
importlib.import_module(name,package=None)
支持绝对导入和相对导入,若是是相对导入,package必须设置
<1>test函数

class A:
    def showme(self):
        print('test模块下的A类')

<2>插件函数

import importlib

#插件函数接
def plugin_load():

    #使用importlib方式导入模块赋值给mod
    mod = importlib.import_module('test')

    #打印这个模块对象
    #print(mod)                         #打印结果:<module 'test' from 'E:\\shishi\\aaa\\m\\test.py'>

    #模块对象mod.A拿属性
    #print(mod.A)                       #打印结果:<class 'test.A'>

    #实例化A类
    getattr(mod, 'A')().showme()       #打印结果:test模块下的A类

if __name__ == '__main__':
    #须要的时候动态加载
    plugin_load()

3.经过importlib实现插件化核心代码
<1>test函数

class A:
    def showme(self):
        print('test模块下的A类')

<2>插件函数

import importlib

#插件函数接收调用的模块名字和调用的类名
def plugin_load(plugin_name:str,sep=":"):
    #三元组方式切割得到想要的模块名字和调用的类名
    m,_,c = plugin_name.partition(sep)

    #模块名字
    #print(m)   #返回结果:test
    #类名名字
    #print(c)   #返回结果:A

    # 使用importlib方式导入模块赋值给mod
    mod = importlib.import_module(m)

    #经过getattr获取test模块对象下的A类
    cls = getattr(mod,c)
    #把这个A类返回
    return cls()

if __name__ == '__main__':
    #第一步:须要的时候动态加载plugin_load函数把调用的模块名字和调用的类传进去
    #最后一步:接收返回的A类showme()实例化
    plugin_load('test:A').showme()            #返回结果:test模块下的A类

4.插件化编程技术
(1)依赖的技术
①反射:运行时获取类型的信息,能够动态维护类型数据
②动态import:程序运行过程当中,接收用户指令或请求,启动相应的插件动态import:推荐使用importlib模块,实现动态import模块的能力
③多线程:能够开启一个线程,等待用户输入,从而加载指定名称的模块
(2)加载的时机
何时加载合适?程序启动的时候,仍是程序运行中呢?
①程序启动时:像pycharm这样的工具,须要不少组件,这些组件多是插件,启动的时候扫描固定的目录,加载插件
②程序运行中:程序运行过程当中,接收用户指令或请求,启动相应的插件
两种方式各有利弊,若是插件过多,会致使程序启动很慢,若是用户须要时再加载,若是插件太大或者依赖多,插件也会启动慢。因此先加载必须的,经常使用的插件,其余插件使用时,发现须要,动态载入。
5.插件化开发的应用
软件的设计不可能作到尽善尽美,或者在某些功能上,不可能作的专业,须要专业的客户本身加强。
(1)接口和插件的区别?
①接口:每每是暴露出来的功能(类的方法或者是函数),实现动态模块加载(操做),例如模块提供的函数或方法,加载模块后调用这些函数完成功能。接口也是一种规范,它约定了必须实现的功能(必须提供某名称的函数),可是不关心怎么实现这个功能。有的地方叫api,是经过一个网址访问后台某一个python模块的某一个类的某一个方法,由于方法才能作事,因此浏览器的某一个地址实际指向的是后台的某一个进行的server里面的一个某一个模块某一个类的某一个方法。通常不关内心面怎么实现,只关心这个操做能作什么事情,这个操做的名称和签名
②插件:是能不能把模块的资源动态的加载进去用。是把模块加载到系统中,运行它,加强当前系统功能,或者提供系统不具有的功能,每每插件技术应用的框架设计中。系统自己设计简单化,轻量级,实现基本功能后,其余功能经过插件加入进来,方便扩展。
经常使用的python标准库模块
包括:(time模块-datetime模块-random模块-pathlib模块-os模块-shutil模块-CSV模块-argparse模块-sys模块-json&pickle&shelve&xml模块-re模块-logging模块-hashlib模块)
time模块
1.三个掌握时间的表达式(时间戳Timestamp-结构化struct_time-字符串时间Fomat string)
(1)时间戳Timestamp表达式

 

 

import time
#秒数,从1970年1月1日开始算到如今
print(time.time())               #输出结果:1517556698.214673

(2)结构化时间struct_time表达式

import time
##时间对象(年,月,周,日,时,分,秒)
print(time.localtime())                   #输出结果:time.struct_time(tm_year=2018, tm_mon=9, tm_mday=5, tm_hour=14, tm_min=17, tm_sec=2, tm_wday=2, tm_yday=248, tm_isdst=0)
t=time.localtime()
#年份
print(t.tm_year)                          #输出结果:2018
#
print(t.tm_wday)                          #输出结果:2
#世界标准时间UTC          
print(time.gmtime())                      #输出结果:time.struct_time(tm_year=2018, tm_mon=2, tm_mday=2, tm_hour=7, tm_min=43, tm_sec=17, tm_wday=4, tm_yday=33, tm_isdst=0)

经过time.mktime将结构化时间转换成时间戳----struct_time转Timestamp

import time
#time.localtime拿到当前结构化时间在用mktime将结构化时间转换成时间戳
print(time.mktime(time.localtime()))        #输出结果:1536128806.0

(3)字符串时间Fomat string表达式(须要拿时间戳,结构化时间转换)
方式一:经过time.strftime将结构化时间转成字符串时间-------struct_time转Fomat string

import time
#time.localtime拿到当前结构化时间,用time.strftime将结构化时间转换成字符串时间
print(time.strftime("%Y-%m-%d %X",time.localtime()))    #输出结果:2018-09-05 14:28:56

方式二:经过time.strptime将字符串时间转换成结构化时间---Fomat string转struct_time

import time
print(time.strptime("2018:09:05:14:36:22","%Y:%m:%d:%X"))   #输出结果:time.struct_time(tm_year=2018, tm_mon=9, tm_mday=5, tm_hour=14, tm_min=36, tm_sec=22, tm_wday=2, tm_yday=248, tm_isdst=-1)

2.显示直观时间的方式
方式一:asctime将结构化时间转化成固定的字符串时间,若是没有参数,将会 time.localtime()做为参数转入

import time
print(time.asctime())     #输出结果:Fri Feb  2 16:20:49 2018

方式二:ctime把时间戳转化成固定的字符串时间,若是没有参数,将会 time.localtime()做为参数转入

import time
print(time.ctime())     #输出结果:Fri Feb  2 16:20:49 2018

3.俩个其它参数
sleep:线程推迟指定的时间运行,单位为秒
clock():在linux系统上返回进程时间,在windows中返回时间差
datetime模块(对日期,时间,时间戳的处理)
1.datetime类
(1)类方法:
<1>today()返回本地时区当前时间的datetime对象

import datetime
time=datetime.datetime.today()
print(time)
##返回:
2019-07-19 10:34:51.816176

<2>now(tz=None)返回当前时间的datetime对象,时间到微秒,若是tz为None,返回和today()同样

import datetime
time = datetime.datetime.now()
print(time)
##返回:
2019-07-19 10:44:05.326834

<3>utcnow()没有时区的当前时间(跟东8区差8个小时)

import datetime
time=datetime.datetime.utcnow()
print(time)
##返回:
2019-07-19 02:37:05.023794

<4>fromtimestamp(timestamp,tz=None)从一个时间戳返回一个datetime对象

import datetime
time = datetime.datetime.now().timestamp()
print('当前时间戳:',time)
t = datetime.datetime.fromtimestamp(int(time))  #时间戳time构造出一个datetime对象
print('经过时间戳返回datetime对象:',t)
##返回:
当前时间戳: 1563505535.150608
经过时间戳返回datetime对象: 2019-07-19 11:05:35

(2)datetime对象
<1>timestamp()返回一个到微秒的时间戳
---时间戳:格林威治时间1970年1月1日0点到如今的秒数

import datetime
time = datetime.datetime.now().timestamp()
print(time)
返回:前面整数部分是到如今的秒数
1563504009.518347

<2>构造方法datetime.datetime(2019,07,19,29,43,79043)
举例:构造一个新时间

>>> import datetime
>>> a = datetime.datetime(2019, 7,19,17,22)
>>> a
datetime.datetime(2019, 7, 19, 17, 22)

<3>year,month,day,hour,minute,second,microsecond,取datetime对象的年月日时分秒及微秒

>>> import datetime
>>> a = datetime.datetime(2019, 7,19,17,22)
>>> a
datetime.datetime(2019, 7, 19, 17, 22)
#取年
>>> a.year
2019
#取日
>>> a.day
19

<4>weekday()返回星期的天,周一0,周日6

>>> import datetime
>>> a = datetime.datetime.now()
>>> a
>>> datetime.datetime(2019, 7, 19, 12, 2, 34, 941509)
>>> a.weekday()
4

<5>isoweekday()返回星期的天,周一1,周日7

>>> import datetime
>>> a = datetime.datetime.now()
>>> a
datetime.datetime(2019, 7, 19, 12, 2, 34, 941509)
>>> a.isoweekday()
5

<6>date()返回日期date对象

>>> import datetime
>>> a = datetime.datetime.now()
>>> a
datetime.datetime(2019, 7, 19, 12, 5, 35, 946047)
>>> a.date()
datetime.date(2019, 7, 19)

<7>time()返回时间time对象

>>> import datetime
>>> a = datetime.datetime.now()
>>> a
datetime.datetime(2019, 7, 19, 12, 5, 35, 946047)
>>> a.time()
datetime.time(12, 5, 35, 946047)

<8>replace()修改并返回新的时间

>>> import datetime
>>> a = datetime.datetime.now()
>>> a
datetime.datetime(2019, 7, 19, 12, 5, 35, 946047)
>>> a.replace(2020)
datetime.datetime(2020, 7, 19, 12, 5, 35, 946047)

<9>isocalendar()返回一个三元组(年,周数,周的天)

>>> import datetime
>>> a = datetime.datetime.now()
>>> a
datetime.datetime(2020, 7, 19, 12, 5, 35, 946047)
>>> a.isocalendar()
(2019, 29, 5)

2.日期格式化
(1)类方法strptime(date_string,format),返回datetime对象
(2)对象方法strftime(format),返回字符串
(3)字符串format函数格式化

import datetime
dt = datetime.datetime.strptime("19/07/19 16:30", "%d/%m/%y %H:%M")
#strftime对象方法
print(dt.strftime("%Y-%m-%d %H:%M:%S"))
#format函数格式化
print("{0:%Y}/{0:%m}/{0:%d} {0:%H}:{0:%M}:{0:%S}".format(dt))
返回:
2019-07-19 16:30:00
2019/07/19 16:30:00

3.timedelta对象
(1)datetime2 = datetime1 + timedelta
(2)datetime2 = datetime1 - timedelta
(3)timedelta = datetime1 - datetime1
(4)构造方法
<1>datetime.timedelta(days=0,seconds=0,microsecond=0,microsecond=0,minutes=0,hours=0,weeks=0)
<2>year = datetime.timedelta(days=365)

import datetime
#当前时间
now = datetime.datetime.now()
print(now)
#构造24小时时间差对象
h = datetime.timedelta(hours=24)
print(h)
#当前时间减去24小时时间
new =datetime.datetime.now() - h
print(new)
返回:
2019-07-19 11:53:45.091903
1 day, 0:00:00
2019-07-18 11:53:45.091903

(5)total_seconds()返回时间差的总秒数

import datetime
#当前时间
now = datetime.datetime.now()
print(now)
#构造24小时时间差对象
h = datetime.timedelta(hours=24)
print(h)
#当前时间减去24小时时间
new =datetime.datetime.now() - h
print(new)
#求当前时间和最新时间的时间差
xx =(datetime.datetime.now() - new).total_seconds()
print(xx)
返回:
2019-07-19 12:01:58.609131
1 day, 0:00:00
2019-07-18 12:01:58.609131
86400.0

random模块(随机模块)
(1)random默认0-1的随机浮点数

import random
ret=random.random()
print(ret)

输出:
0.4613546703901257
(2)random.randin随机取出范围里的值

import random
ret=random.randint(1,3) #1,2,3里随机取值 print(ret)

输出:
1或2或3
(3)random.randrange随机取出范围里的值不包括最后一个值

import random
ret=random.randrange(1,3) #随机取出1-2里的值 print(ret)

输出:
1或2
(4)random.choice随机取出里列表里的某一个值

import random
ret=random.choice([11,22,33,44,55]) #随机选一个 print(ret)

输出:
11或22或33或44或55
(5)random.sample随机取出里列表里的某俩个值

import random
ret=random.sample([11,22,33,44,55],2) #随机选俩个 print(ret)

输出:
[44, 11]
(6)random.uniform随机取任意范围浮点数

import random
ret=random.uniform(1,4) print(ret)

输出:
2.4613546703901257
(7)random.shuffle打乱顺序

ret=[1,2,3,4,5]
random.shuffle(ret)
print(ret)

输出:
[4, 2, 1, 5, 3]
举例:随机生成5数字跟字母相拼的位验证码功能:

import random

def v_code():
    ret=""
    for i in range(5):
        num=random.randint(0,9)
        alf=chr(random.randint(65,122))
        s=str(random.choice([num,alf]))
        ret+=s
    return ret
print(v_code())

思路:
定义一个结果字符串初始值为ret=""
循环,验证码有几位循环几回for i in range(5):
随机数字生成:random.randint(0,9)
随机字母经过chr把数字转换成对应字母生成:chr(random.randint(65,122))
随机取出数字和字母的值(random.choice([num,alf])),str转换一下
拼接起来ret+=s
最后返回return ret
pathlib模块(文件目录操做)
from pathlib import Path
1.目录操做
(1)当前目录

>>> from pathlib import Path
>>> p = Path()
>>> print(p.absolute())
/root

(2)当前目录下的a/b/c/d

>>> from pathlib import Path
>>> p = Path('a','b','c/d')
>>> print(p)
a/b/c/d

(3)根下的etc目录

>>> from pathlib import Path
>>> p = Path('/etc')
>>> print(p)
/etc

2.路径拼接和分解
(1)操做符/
Paht对象 / Path对象
Path对象 / 字符串或者字符串 / Paht对象

#当前路径
>>> from pathlib import Path
>>> p = Path('a','b')
#当前p路径
>>> p
PosixPath('a/b')
#加上c/d
>>> p = p / 'c' / 'd'
>>> p
PosixPath('a/b/c/d')
#加上e
>>> p /= 'e'
>>> p
PosixPath('a/b/c/d/e')

(2)分解
parts属性,能够返回路径中的每个部分
(3)joinpath
joinpath(*other)链接多个字符串到Path对象中

>>> from pathlib import Path
>>> p = Path()
>>> print(p.absolute())      #当前路径
/root
>>> p = p.joinpath('a','b')  #返回新路径
>>> print(p.absolute())
/root/a/b

3.获取路径
str获取路径字符串
bytes获取路径字符串的bytes

>>> from pathlib import Path
>>> p = Path('/etc')
>>> print(str(p),bytes(p))
/etc b'/etc'

4.父目录
parent目录的逻辑父目录
parents父目录序列,索引0是直接的父
(1)查看父目录

>>> from pathlib import Path
>>> p = Path('/etc')
>>> p
PosixPath('/etc')
#查看父目录
>>> p.parents                   
PosixPath('/')

(2)查看每层父目录

>>> from pathlib import Path
>>> p = Path('a','b','c/d')
>>> p
PosixPath('a/b/c/d')
#查看每层父目录,absolute转化成绝对路径
>>> list(p.absolute().parents)
[PosixPath('/root/a/b/c'), PosixPath('/root/a/b'), PosixPath('/root/a'), PosixPath('/root'), PosixPath('/')]
#迭代查看第一层父目录
>>> next(iter(p.absolute().parents))
PosixPath('/root/a/b/c')

5.处理路径用到的方法
(1)name目录的最后一个部分

>>> p =Path('/etc/sysconfig/network/xx.cfg.gz')
>>> p.name
'xx.cfg.gz'

(2)suffix目录中最后一个部分的扩展名

>>> p =Path('/etc/sysconfig/network/xx.cfg.gz')
>>> p.suffix
'.gz'

(3)stem目录最后一个部分,没有后缀

>>> p =Path('/etc/sysconfig/network/xx.cfg.gz')
>>> p.stem
'xx.cfg'

(4)suffixes返回多个扩展名列表

>>> p =Path('/etc/sysconfig/network/xx.cfg.gz')
>>> p.suffixes
['.cfg', '.gz']

(5)with_suffix(suffix)补充扩展名到路径尾部,分会新的路径,扩展名存在则无效

#把扩展名gz换成tar
>>> p =Path('/etc/sysconfig/network/xx.cfg.gz')
>>> p.with_suffix('.tar')
PosixPath('/etc/sysconfig/network/xx.cfg.tar')

(6)with_name(name)替换目录最后一个部分返回一个新的路径

#把最后一部分名字改成ii.ifg
>>> p =Path('/etc/sysconfig/network/xx.cfg.gz')
>>> p.with_name('ii.ifg')
PosixPath('/etc/sysconfig/network/ii.ifg')

(7)cwd()返回当前工做目录

>>> from pathlib import Path
#当前描述路径
>>> p = Path('a','b','c/d')
>>> p
PosixPath('a/b/c/d')
#当前工做目录
>>> p.cwd()
PosixPath('/root')

(8)home()返回当前家目录

>>> p = Path('a','b','c/d')
>>> p
PosixPath('a/b/c/d')
#当前家目录
>>> p.home()
PosixPath('/root')

(9)is_dir()是不是目录

#给/tmp下建立一个lysql.tar.gz文件
>>> p = Path('/tmp/lysql.tar.gz')
>>> p.touch()
#查看/tmp下的lysql.tar.gz是不是目录
>>> p.is_dir()
False

(10)is_file()是不是普通文件

#给/tmp下建立一个lysql.tar.gz文件
>>> p = Path('/tmp/lysql.tar.gz')
>>> p.touch()
#查看/tmp下的lysql.tar.gz是不是普通文件
>>> p.is_file()
True

(11)is_socket()是不是socket文件
(12)is_block_device()是不是块设备
(13)is_char_device()是不是字符设备
(14)is_absolute()是不是绝对路径
(15)resolve()返回一个新的路径,这个新路径就是当前Path对象的绝对路径,若是是软连接则直接被解析

>>> from pathlib import Path
#给/tmp下建立一个lysql.tar.gz文件
>>> p = Path('/tmp/lysql.tar.gz')
>>> p.touch()
#返回lysql.tar.gz文件的绝对路径
>>> p.resolve()
PosixPath('/tmp/lysql.tar.gz')

(16)absolute()也能够获取绝对路径,可是推荐使用resolve()
(17)exists()目录或文件是否存在
(18)rmdir()删除空目录,没有提供判断目录为空的方法
(19)touch(mode=0o666,exist_ok=True)建立一个文件

>>> from pathlib import Path
#给/tmp下建立一个lysql.tar.gz文件
>>> p = Path('/tmp/lysql.tar.gz')
>>> p.touch()

(20)as_uri()将路径返回成URL,列如'file:///etc/passwd'

#将路/tmp/lysql.tar.gz径返回成URL
>>> p.as_uri()
'file:///tmp/lysql.tar.gz'

(21)mkdir(mode=0o777,parents=False,exist_ok=False)
参数:parents,是否建立父目录,True等同于mkdir -p;False时,父目录不存在,则抛出FileNotFoundError
参数:exist_ok参数,在3.5版本加入。False时,路径存在,抛出FileExistsError;True时,FileExistsError被忽略

>>> from pathlib import Path
#给/tmp下建立一个xixi目录
>>> p = Path('/tmp/xixi')
>>> p.mkdir(parents=True)

(22)iterdir()迭代当前目录

>>> from pathlib import Path
#迭代当前目录
>>> for x in Path().iterdir():
#判断是不是目录打印出来
...     if x.is_dir():
...         print(x)

举例:遍历,并判断文件类型,若是是目录是否能够判断其是否为空

from pathlib import Path
p = Path('/etc')    
#循环/etc全部的父目录路径
for x in p.parents[len(p.parents)-1].iterdir():
    print(x,end='\t')                 #打印/etc全部的父目录
    if x.is_dir():                    #判断是不是目录
        flag = False
        for _ in x.iterdir():         #迭代目录判断是不是空目录
            flag = True
            break
        #for循环是否可使用else子句
        print('dir','不为空' if flag else '为空' , sep='\t')
    elif x.is_file():   #不然判断是不是文件
        print('文件')
    else:
        print('其它文件')

6.通配符
(1)glob(pattern)通配给定的模式
①当前目录

>>> from pathlib import Path
#当前目录下x*开头的全部文件或目录
>>> list(Path().glob('x*'))
[PosixPath('xx.py'), PosixPath('xxx.txt'), PosixPath('x.txt'), PosixPath('xixi.txt'), PosixPath('xixi')]

②当前目录下一层目录

>>> from pathlib import Path
#当前目录下的下层目录下*.py开头的全部文件或目录
>>> list(Path().glob('*/*.py'))
[PosixPath('xixi/xx.py')]

③当前目录下层全部目录

>>> from pathlib import Path
#当前目录算起往下任意目录*.py开头的全部文件或目录
>>> list(Path().glob('**/*.py'))
[PosixPath('xx.py'), PosixPath('xixi/xx.py')]

(2)rglob(pattern)通配给定的模式,递归目录

>>> from pathlib import Path
#递归查找当前目录算起往下任意目录*.py开头的全部文件或目录
>>> list(Path().rglob('*.py'))
[PosixPath('xx.py'), PosixPath('xixi/xx.py')]

7.匹配
(1)match(pattern)
模式匹配,成功返回True

>>> from pathlib import Path
匹配当前目录是否有*.py的文件或目录
>>> Path('.py').match('*.py')
True

8.文件操做
(1)open(mode='r',buffering=-1,encoding=None,errors=None,newline=None):使用方法相似内建函数open。返回一个文件对象
(2)read_bytes():以'rb'读取路径对应文件,并返回二进制流
(3)read_text(encoding=None,errors=None):以'rt'方式读取路径对应文件,返回文本

>>> from pathlib import Path
#建立一个xixi.txt文件
p = Path('/tmp/xixi/xixi.txt')
#写(每次清空)
>>> p.write_text('xixi')
4
#
>>> p.read_text()
'xixi'
(4)Path.write_bytes(data):以'wb'方式写入数据到路径对应文件
(5)write_text(data,encoding=None,errors=None):以'wt'方式写入字符串到路径对应文件。
>>> from pathlib import Path
#建立一个xixi.txt文件
p = Path('/tmp/xixi/xixi.txt')
#写(每次清空)
>>> p.write_text('xixi')
4

os模块(是与操做系统交互的一个接口(操做系统打交道)
(1)os.getcwd()获取当前工做目录,即当前python脚本工做的目录路径

import os
print(os.getcwd())

输出:
E:\cc\os
(2)os.chdir('dirname')改变当前脚本工做目录;至关于shell下cd

import os
print(os.getcwd())   #当前目录
os.chdir("..")       #至关于cd ..
print(os.getcwd())   #返回上层目录

输出:
E:\cc\os
E:\cc
(3)os.makedirs('dirname1/dirname2')可建立多层递归目录,

import os
os.makedirs('dirname1/dirname2') #当前目录下建立dirname1目录里面在建立dirname2目录

(4)os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,若也为空,则删除,以此类

import os
os.removedirs('dirname1/dirname2')  #删除dirname1和里面的dirname2(若是dirname1里有文件或目录,则不会被删除)

(5)os.mkdir('dirname') 生成单级目录;至关于shell中mkdir dirname

import os
os.mkdir('dirname')   #当前目录下建立dirname目录

(6)os.rmdir('dirname') 删除单级空目录;若目录不为空则没法删除

import os
os.rmdir('dirname')    #删除当前目录下的dirname(若是dirname里有文件或目录,则不会被删除)

(7)os.listdir('dirname')列出指定目录下的全部文件和子目录,包括隐藏文件,并以列表方式打印

import os
print(os.listdir())  

打印:
['dirname', 'os_test.py']
(8)os.remove()删除一个文件

import os
os.remove('xixi.txt')  #删除当前目录下xixi.txt文件

(9)os.rename("oldname","newname")递归移动文件

>>> import os
#把tmp目录下的xixi目录里的东西拷贝到otp目录
>>> os.rename('/tmp/xixi','/otp')

(10)os.stat('path/filename')获取文件/目录信息

import os
print(os.stat("xixi.txt"))   #获取xixi.txt的文件信息

输出:
posix.stat_result(st_mode=33188, st_ino=3936489, st_dev=64768L, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1535704707, st_mtime=1535704707, st_ctime=1536133819)
(11)os.sep 输出操做系统特定的路径分隔符win下为"\\" ,linux下为"/"
(12)os.linesep 输出当前平台使用的行终止符win下为"\r\n",linux下为"\n"
(13)os.name 输出字符串指示当前使用平台
(14)os.system("bash command")运行shell命令,直接显示

import os
os.system("ls")             #至关于当前目录下执行ls命令

(15)os.environ获取系统环境变量
(16)os.path.split(path)将path分割成目录和文件名二元组返回

import os
print(os.path.split("E:\cc\os\os_test.py"))   #把路径和名字分开以元祖方式显示出来

输出:
('E:\\cc\\os', 'os_test.py')
(17)os.path.dirname(path)返回path的目录

import os
print(os.path.dirname("E:\cc\os\os_test.py"))   #取os_test.py的文件路径

输出:
E:\cc\os
(18)os.path.basename(path) 返回path最后的文件名。

import os
print(os.path.basename("E:\cc\os\os_test.py"))   #取os_test.py文件名字

输出:
os_test.py
(19)os.path.exists(path)若是path存在,返回True;若是path不存在,返回False
(20)os.path.isabs(path)若是path是绝对路径,返回True
(21)os.path.isdir(path)若是path是一个存在的目录,则返回true。
(22)os.path.join(paht1[,path2[, ...]])将多个路径组合返回,第一个绝对路径以前的参数将被忽略
路径是:E:\cc\os\os_test.py

import os
a="E:\cc"
b="os\os_test.py"
os.path.join(a,b)   #拼接好的路径E:\cc\os\os_test.py

(23)os.path.getatime(path) 返回path所指向的文件或目录的最后存取时间
(24)os.path.getmtime(path) 返回path所指向的文件或目录的最后修改时间
shutil模块(文件目录拷贝)
1.拷贝
(1)copyfileobj(fsrc,fdst[,length])文件对象的复制,fsrc和fdst是open打开的文件对象,复制内容。fdst要求可写
length指定了表示buffer的大小;

>>> from pathlib import Path
#建立test文件写入abcde
>>> Path('test').write_text('abcde')
5
>>> Path('test').read_text()
'abcde'
>>> import shutil
>>> with open('test') as f1:
...     with open('test2','w') as f2:
#经过shutil模块把test内容拷贝到test2中
...         shutil.copyfileobj(f1,f2)

(2)copyfile(src,dst,*,follow_symlinks=True):复制文件内容,不含元数据。src,dst为文件的路径字符串
本质山调用的就是copyfileobj,因此不带元数据二进制内容复制。

>>> import shutil
>>> shutil.copyfile('test','test2')
'test2'

(3)copymode(src,dst,*,follow_symlinks=True):复制权限

>>> import shutil
>>> shutil.copymode('test','test2')

(4)copystat(src,dst,*follow_symlinks=True):复制元数据,stat包含权限

>>> import shutil
>>> shutil.copystat('test','test2')

(5)copy(src,dst,*,follow_symlinks=True):复制文件内容,权限和部分元数据,不包括建立时间和修改时间。
本质上是调用:copyfile(src,dst,*,follow_symlinks=True)和copymode(src,dst,*,follow_symlinks=True)

>>> import shutil
>>> shutil.copy('test','test2')

(6)copy2比copy多了个复制所有元数据,但须要平台支持
本质上调用的是:copyfile(src,dst,*,follow_symlinks=True)和copystat(src,dst,*follow_symlinks=True)

>>> import shutil
>>> shutil.copy2('test','test2')

(7)copytree(src,dst,symlinks=False,ignore=None,copy_function=copy2,ignore_dangling_symlinks=False):递归复制目录。默认使用copy2,也就是带多的元数据复制
scr,dst必须是目录,src必须存在,dst必须不存在
举例:

import shutil
#把tmp下的xixi里的全部东西拷贝到otp下的aaa里,且aaa目录不能存在
>>> shutil.copytree('/tmp/xixi','/otp/aaa')
'/otp/aaa'

ignore = func,提供一个callable(src,names)->ignored_names。提供一个函数,它会被调用。src是源目录,nmaes是os.listdir(src)的结果,就是列出src中的文件名,返回值是要被过滤的文件名的set类型数据。
举例:

import shutil
def ignore(src, names):
    #filter传两个参数:1.函数。2.迭代的东西
    ig = filter(lambda x: x.startswith('a'),names) #判断文件名必须以a开头的文件或目录不拷贝
    return set(ig)
#把tmp目录下的xixi目录里的东西拷贝到top目录下的abc目录(abc目录不能存在)
shutil.copytree('/tmp/xixi','/otp/abc',ignore=ignore)

2.删除:
shutil.rmtree(path,ignore_errors=False,onerror=None):递归删除。如同rm -rf同样危险。
它不是原子操做,有可能删除错误,就会中断,已经删除的就删除了。
ignore_errors为true,忽略错误。当为False或者omitted时onerror生效。
onerror为callable,接受函数function,path和execinfo。

>>> import shutil
#删除otp目录下的abc
>>> shutil.rmtree('/otp/abc')   #相似rm -rf

3.move移动
move(src,dst,copy_function=copy2):递归移动文件,目录到目标,返回目标
自己使用的是os.rename方法
若是不支持rename,入股欧式目录则想copytree在删除源目录。
默认使用copy2方法。

>>> import os
#把tmp目录下的xixi目录里的东西拷贝到otp目录
>>> os.rename('/tmp/xixi','/otp')

CSV模块
一.CSV文件
1.csv文件简介
(1)逗号分隔值Comma-Separated Values
(2)CSV是一个被行分隔符,列分隔符划分红行和列的文本文件
(3)没有指定字符编码
2.行分隔符为\r\n,最后一行能够没有换行符
(1)列分隔符常为逗号或者制表符
(2)每一行成为一条record(记录)
3.字段可使用双引号括起来,也能够不使用。入股字段中出现了双引号,逗号,换行符必须使用双引号括起来。入股字段的值是双引号,使用两个双引号表示一个转义
(1)表头可选,和字段列对齐就好了

from pathlib import Path
#构建文件对象
path = 'e:/wangshiyao/aaa/xixi.csv'
p = Path(path)

#判断是否有这个路径
if not p.parent.exists():
    p.parent.mkdir(parents=True)

#写入内容
line = '''\
id,name,age,comment
1,dongdong,18,中国
2,xixi,"I'm 23",,
3,nannan,33,"this is a ""test"" string."
4,beibei,"中国

人民
"
'''

p.write_text(line)

返回:

二.CSV模块
1.reader(csvfile,dialect='excel',**fmtparams):返回DictReader的实例,是个行迭代器。
(1)delimiter列分隔符,逗号
(2)lineterminator行分隔符\r\n
(3)quotechar字段的引用符号,缺省为",双引号
2.双引号的处理:
(1)doublequote双引号的处理,默认为True.若是和quotechar为同一个,True,则使用2个双引号表示
(2)False表示使用转义字符将做为双一号的前缀
(3)escapechar一个转义字符,默认为None。
(4)quoting指定双引号的规则。QUOTE_ALL全部字段;QUOTE_MINIMAL特殊字符字段;
(5)QUOTE_NONNUMERIC非数字字段;QUOTE_NONE都不使用引号
3.writer(csvfile,dialect='excel',**fmtparams):返回DictWriter的实例
4.主要方法有writerow,writerows

from pathlib import Path
import csv

#构建文件对象
path = 'e:/wangshiyao/aaa/xixi.csv'
p = Path(path)

#判断是否有这个路径
if not p.parent.exists():
    p.parent.mkdir(parents=True)

#写入内容
line = [1,"xixi",20]
line2 = [(2,'dongdong',18),(3,'nannan',23),(4,'beibei',19)]

#若是有这个路径建立这个文件
with open(path,'w',newline='') as f:  #newline=''去除空行
    #给它一个文件对象writer写入
    writer = csv.writer(f)
    #writerow方法
    writer.writerow(line)
    #writerows方法
    writer.writerows(line2)

with open(path) as f:
    #返回可迭代对象能够读取
    reader = csv.reader(f)
    #print(type(reader))
    for line in reader:
        if line:
            print(line)
###返回内容:
['1', 'xixi', '20']
['2', 'dongdong', '18']
['3', 'nannan', '23']
['4', 'beibei', '19']

csv文件内容:

argparse模块(为脚本传递命令参数功能)
1.argparse模块初识:一个可执行文件或者脚本均可以接收参数。
举例:ls -l /etc
/etc:是位置参数
-l:是短选项
如何把这些参数传递给程序呢?
从3.2开始python提供了参数分析的模块argparse。
2.参数分类分为:
位置参数,参数放在那里,就要对应一个参数位置。例如/etc就是对应一个参数位置。
选项参数,必须经过前面是-的短选项或者-的长选项,而后后面的才算它的参数,固然短选项后面也能够没有参数。
3.基本解析
(1)一个简单解析器:

import argparse
parser = argparse.ArgumentParser()   #构造解析器
args = parser.parse_args()           #解析参数
parser.print_help()
运行:python3 xx.py -h
####打印结果:
usage: xx.py [-h]
optional arguments:
  -h, --help  show this help message and exit

(2)总结:
argparse不只仅作了参数的定义和解析,还自动帮助生成了帮助信息。尤为是usage,能够看到如今定义的参数是不是本身想要的
4.解析器的参数
(1)prog程序的名字,缺省使用sys.argv[0]
(2)add_help自动为解析器增长-h和-help选项,默认为True
(3)description为程序功能添加描述
5.位置参数解析
ls基本功能应该解决目录内容的打印
打印的时候应该指定目录路径,须要位置参数
程序定义为:
ls [-h] path
-h为帮助,无关紧要
path为位置参数,必须提供
6.传参
parse_args(args=None,namespace=None)
args参数列表,一个可迭代对象。内部会把可迭代对象转换成list。若是为None则使用命令行传入参数,非 None则使用args参数的可迭代对象

from pathlib import Path
import argparse
#
def showdir(path:str='.'):      #接收目录
    p = Path(path)
    for file in p.iterdir():    #返回迭代器对象
        print(file.name)

parser = argparse.ArgumentParser(prog='ls', add_help=True ,description='列出全部文件')  # 构造解析器
parser.add_argument('path')  # 位置参数跟传参对应
parser.add_argument('-l')
parser.add_argument('-a','--all')

if __name__ == '__main__':
    #showdir('E:/wangshiyao/aaa')
    args = parser.parse_args("/etc".split())   #分析参数,同时传入可迭代的参数
    parser.print_help()                        #打印帮助

    print('args=',args)                        #打印名次空间中收集的参数

####运行结果: usage: ls [-h] [-l L] [-a ALL] path 列出全部文件 positional arguments: #有一个位置参数定义的path path optional arguments: -h, --help show this help message and exit #自动化定义的帮助 -l L #有一个选项-l后面必须接参数 -a ALL, --all ALL #定义的-a和--all后面必须也接收参数 args= Namespace(all=None, l=None, path='/etc') #解析后会把args放到Namespace对象里面

7.非必须位置参数
上面代码必须输入位置参数,不然会报错。但有时候,ls命令不输入任何路径的话就表示列出当前目录的文件列表

from pathlib import Path
import argparse
import datetime
import stat        #解决文件模式和文件类型

def listdir(path='.',all=False,detail=False,human=False):
    #实现-h文件大小
    def _get_human(size:int):
        units = ['','K','M','G','T','P']
        depth = 0

        while size >= 1000:
            size = size // 1000
            depth +=1

            print(size,units[depth])

        return "{}{}".format(size,units[depth])


    def _showdir(path='.',all=False,detail=False,human=False):      #接收目录
        #l列出文件和目录
        p = Path(path)
        for file in p.iterdir():                     #返回迭代器对象
            #解决all的问题
            if not all:                              #判断是.开头的不打印
                if str(file.name).startswith('.'):   #若是是点开头的
                    continue                        #跳过
            #解决detail详情打印(-l问题)
            if detail:                               #若是你是详情打印
                #-rw-r--r--  1 root root    228911 Sep  5 09:47
                #处理-h大小
                st = file.stat()
                h =st.st_size
                if human:
                    h = _get_human(st.st_size)

                yield (stat.filemode(st.st_mode),st.st_nlink,st.st_uid,st.st_gid,h,datetime.datetime.fromtimestamp(st.st_atime).strftime('%Y-%m-%d %H:%M:%S'),file.name)
            else:                                    #若是不是详情打印(没有-l)
                yield (file.name,)                   #直接打印文件名

    #排序
    yield from sorted(_showdir(args.path, args.all, args.l, args.h), key=lambda x: x[-1])

parser = argparse.ArgumentParser(prog='ls', add_help=False ,description='列出全部文件')  # 构造解析器
parser.add_argument('path',nargs='?',default='.',help='paht help')  # 位置参数跟传参对应
parser.add_argument('-l',action='store_true')
parser.add_argument('-h',action='store_true')
parser.add_argument('-a','--all',action='store_true')

if __name__ == '__main__':
    args = parser.parse_args(('E:/wangshiyao/aaa','-lh'))   #分析参数,同时传入可迭代的参数
    parser.print_help()                        #打印帮助
    print('args=',args)                       #打印名次空间中收集的参数

    for st in listdir(args.path,args.all,args.l,args.h):
         print(st)

####打印结果:
usage: ls [-l] [-h] [-a] [path]

列出全部文件

positional arguments:
  path       paht help

optional arguments:
  -l
  -h
  -a, --all
args= Namespace(all=False, h=True, l=True, path='E:/wangshiyao/aaa')
2 K
9 K
5 K
('-rw-rw-rw-', 1, 0, 0, '2K', '2019-09-17 12:10:52', '1.py')
('-rw-rw-rw-', 1, 0, 0, '9K', '2019-09-17 11:14:21', '2.py')
('-rw-rw-rw-', 1, 0, 0, '269', '2019-09-06 14:12:34', 'a.json')
('-rw-rw-rw-', 1, 0, 0, '234', '2019-09-05 17:18:46', 'mysql.ini')
('-rw-rw-rw-', 1, 0, 0, '234', '2019-09-05 17:11:16', 'mysql_new.ini')
('drwxrwxrwx', 1, 0, 0, '0', '2019-09-09 12:13:05', 'xi')
('-rw-rw-rw-', 1, 0, 0, '15', '2019-09-06 16:00:59', 'xixi.txt')
('-rw-rw-rw-', 1, 0, 0, '5K', '2019-09-16 17:33:39', 'xx.log')

sys模块(解释器打交道)
1.sys.argv:命令行参数list,第一个元素是程序自己路径
import sys
arg=sys.argv[1]
        if arg == "stop":  #启动程序加stop
            stop()
        elif arg == "start":
            start()

2.sys.exit(n):退出程序,正常退出时exit(0)
3.sys.version:获取python解释程序的版本信息
4.sys.maxint:最大int值
5.sys.modules:解释器初始化的时候,会初始化sys.modules字典(保存已加载的模块)
6.sys.path:模块搜索顺序

import sys

for p in sys.path:
    print(p)
    
##返回搜索顺序:
E:\shishi\aaa\xi                          #程序运行的主程序脚本所在的目录
E:\shishi\aaa                             #python的path路径
E:\python3\python35.zip
E:\python3\DLLs
E:\python3\lib
E:\python3
E:\python3\lib\site-packages              #第三方的包放在这下面
E:\python3\lib\site-packages\win32
E:\python3\lib\site-packages\win32\lib
E:\python3\lib\site-packages\Pythonwin

总结:
①显示结果为,python模块的路径搜索顺序:
当加载一个模块的时候,须要从这些搜索路径中从前到后依次查找,不搜索这些目录的子目录,搜索到就加载,搜索不到就抛异常
②路径能够为字典,zip文件,egg文件:
.egg文件,由setuptools库建立的包,第三方库经常使用的格式。添加了元数据(版本号,依赖项等)信息的zip文件
③路劲顺序为:
程序运行的主程序脚本所在的目录
PYTHONPATH目录,环境变量PYTHONPATH设置的目录也是搜索模块的路径
标准库目录,Python自带的库模块所在目录
④sys.path能够被修改,追加新的目录
7.sys.platform:返回操做系统平台名称
8.sys.stdout.flush()刷新缓存:进度条

import sys
import time
for i in range(100):
    sys.stdout.write("#")
    time.sleep(0.1)
    sys.stdout.flush()

json&pickle&shelve&xml模块(序列化与反序列化)
1.为何要序列化
(1)内存中的字典,链表如何保存到一个文件中?
(2)若是是本身定义的类的实例,如何保存到一个文件中?
(3)如何从文件中读取数据,并让它们在内存中再次变成本身对应的类的实例?
2.什么是序列化和反序列化:要设计一套协议,按照某种规则,把内存中数据保存到文件中。文件是一个字节序列,因此必须把数据转换成字节序列,输出到文件 这就是序列化,反之,从文件的字节序列恢复到内存,就是反序列化。
(1)序列化(serialization):把对象(变量)从内存中变成可存储或传输的过程为序列化,序列化以后,就能够把序列化的内容写入到磁盘,或者经过网络传输到别的机器上。->二进制
(2)反序列化(deserialization):把变量内容从序列化的对象从新读取到内存里称之为反序列化。<-二进制
3.持久化:序列化保存到文件就是持久化
可将数据序列化后持久化,或者网络传输;也能够将从文件中或者网络接收到字节序列反序列化
4.python程序之间能够都是pickle解决序列化,反序列化,若是是跨平台,跨语言,跨协议pickle就不适合了,就须要公共协议。如XML,Json,Protocol Buffer等
5.应用:本地序列化的清空,应用较少。通常来讲,大多数场景都应用在网络中。将数据序列化后经过网络传输到远程节点,远程服务器上的服务将接收到的数据反序列化后,就可使用了。可是要注意一点,远程接收端,反序列化时必须有对一个的数据类型,不然就会报错,尤为是自定义类,必须远程的有
pickle模块
(1)dumps对象序列化
(2)dump对象序列化到文件对象,就是存入文件
(3)loads对象反序列化
(4)load对象反序列化,从文件读取数据
(5)序列化反序列举例:

import pickle
###定义列表
lis = 'a b c'.split()
#print(lis)                             #返回的是列表:['a', 'b', 'c']

###定义字典
d = dict(zip('abc',range(3)))
#print(d)                              #返回的是字典{'b': 1, 'a': 0, 'c': 2}

###序列化
with open('xixi.txt','wb+') as f:   #注意是w是写入str,wb是写入bytes,lis和d是'bytes'
   #第一次序列化写入列表
   pickle.dump(lis,f)                  #等价于pickle.dump(lis,f)--->传到xixi.txt文档中
   #第二次序列化写入字典
   pickle.dump(d,f)                    #等价于pickle.dump(d,f)--->传到xixi.txt文档中

###反序列化
with open('xixi.txt','rb') as f:
   #第一次反序列化读取列表数据
   tmp = pickle.load(f)              #经过pickle.loads()反序列化
   print(tmp)                        #['a', 'b', 'c']
   #第二次反序列化读取字典数据
   tmp = pickle.load(f)              #经过pickle.loads()反序列化
   print(tmp)                        #{'c': 2, 'b': 1, 'a': 0}
#返回:
['a', 'b', 'c']
{'b': 1, 'a': 0, 'c': 2}

shelve模块
shelve模块比pickle模块简单,只有一个open函数,返回相似字典的对象,可读可写:key必须为字符串,而只能够是python所支持的数据类型
(1)传进去:把一个字典放入文本 f={}

import shelve
f = shelve.open(r'xixi')                    #会生成三个文本xixi.bak和xixi,dat和xixi,dir              
f['stu1_info']={'name':'shishi','age':'18'}
f['stu2_info']={'name':'alyaoyao','age':'20'}
f['school_info']={'website':'oldboyedu.com','city':'beijing'}
f.close()                                   #关闭f.close把三个字典{'name':'shishi','age':'18'},{'name':'alyaoyao','age':'20'},{'website':'oldboyedu.com','city':'beijing'}放入文本xixi里

(2)读取出来:将一个字典放入文本 f={}

import shelve
f = shelve.open(r'xixi')          
print(f.get('stu1_info')['age'])   #读取'age':'18'
#读取结果:
18

json模块
一.JSON
1.JSON(JavaScript Object Notation,JS对象标记)是一种轻量级的数据交换格式。它基于ECMAScript(w3c指定的JS规范)的一个子集,采用彻底独立对于编程语言的文本格式来存储和表示数据
2.JSON的数据类型
-值:双引号引发来的字符串,数值,true和false,null,对象(object),数组(array),这些都是值。
(1)字符串:由双引号包围起来的任意字符的组合,能够有转义字符。
(2)数值:有正负,有整数,浮点数
(3)对象:无序的键值对的集合
格式:{key 1:value 1,...,keyn:valulen}
key必须是一个字符串,须要双引号包围这个字符串。value能够是任意合法的值
(4)数组:有序的值的集合
格式:[val1,...,valn]
二.json模块
1.python支持少了内建数据类型到Json类型的转换。

2.经常使用方法
(1)dumps json编码
(2)dump json编码并存入文件
(3)loads json解码
(4)load json解码,从文件读取数据
3.举例:
(1)把字典类型里的全部转换成json类型

import json
d = {'a':123,'b':['abc',{'c':234}],'d':True,'e':False,'f':None}
print('打印字典类型:',d)
print('转换json类型:',json.dumps(d))
#打印:
打印字典类型: {'e': False, 'd': True, 'a': 123, 'b': ['abc', {'c': 234}], 'f': None}
转换json类型: {"e": false, "d": true, "a": 123, "b": ["abc", {"c": 234}], "f": null}

(2)经过json.dumps把字典转换成json字符串写到文本里

import json
a={'name':'xixi'}
f=open("new_hello","w")

a_str=json.dumps(a)   #把{'name':'xixi'}转换成json字符串{"name": "xixi"}--->'{"name": "xixi"}'赋值给a_str
f.write(a_str)        #经过f.write把a_str写到文件new_hello里
#写入结果:
{"name": "xixi"}

(3)经过json.loads()反序列化变换成原来的数据读取出来

import json
f_read=open("new_hello","r")
data=json.loads(f_read.read())   #经过json.loads把{"name": "xixi"}变回成字典
print(data)                      
print(data["name"])print(type(data))
#输出结果:
{'name': 'xixi'}
xixi
<class 'dict'>

通常json编码的数据不多落地,数据都是经过网络传输。传输的时候。要考虑压缩它。本质上来讲它就是个文本,就是个字符串。
json很简单,几乎语言变成都支持Json,全部应用十分普遍
xml模块
xml是实现不一样语言或程序程序之间进行数据交换的协议,跟json差很少,但json使用起来更简单
xml的格式:就是经过<>节点来区别数据结构

#xml文档(xml_lesson)
<
data> #date是一个根对象,下面三个country是节点对象 <country name="Liechtenstein"> <rank updated="yes">2</rank> <year updated="yes">2010</year> <gdppc>141100</gdppc> <neighbor direction="E" name="Austria" /> <neighbor direction="W" name="Switzerland" /> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year updated="yes">2013</year> <gdppc>59900</gdppc> <neighbor direction="N" name="Malaysia" /> </country> <country name="Panama"> <rank updated="yes">69</rank> <year updated="yes">2013</year> <gdppc>13600</gdppc> <neighbor direction="W" name="Costa Rica" /> <neighbor direction="E" name="Colombia" /> </country> </data>

1.查询:
(1)查询跟根对象

import xml.etree.ElementTree as ET  #用ET这个名字代替xml.etree.ElementTree

tree = ET.parse("xml_lesson")       #经过这个模块下的parse解析方法把xml_lesson数据解析开拿到对象tree
root = tree.getroot()               #拿到根节点就拿到根对象
print(root.tag)                     #经过tag打印根对象标签名字

查询结果:date
(2)查询三个子对象标签名字

for i in root:     #经过for循环根对象打印三个子对象标签名字
     print(i.tag)   

查询结果:
country
country
country
(3)查询三个子对象country里面的全部标签名字

for i in root:           #经过双层for循环打印三个子对象country里面的全部标签名字
     for j in i:
         print(j.tag)

查询结果:
rank
year
gdppc
neighbor
neighbor
rank
year
gdppc
neighbor
rank
year
gdppc
neighbor
neighbor
(4)查询三个子对象的标签属性

for i in root:   
     print(i.attrib)

查询结果:
{'name': 'Liechtenstein'}
{'name': 'Singapore'}
{'name': 'Panama'}
(4)查询三个子对象里全部的标签属性是什么

for i in root:
     print(i.attrib)
     for j in i:
         print(j.attrib)

查询结果:
{'updated': 'yes'}
{'updated': 'yes'}
{}
{'direction': 'E', 'name': 'Austria'}
{'direction': 'W', 'name': 'Switzerland'}
{'updated': 'yes'}
{'updated': 'yes'}
{}
{'direction': 'N', 'name': 'Malaysia'}
{'updated': 'yes'}
{'updated': 'yes'}
{}
{'direction': 'W', 'name': 'Costa Rica'}
{'direction': 'E', 'name': 'Colombia'}
(5)查询三个子对象里全部实际内容

for i in root: 
     for j in i:
         print(j.text)

查询结果:
2
2010
141100
None
None
5
2013
59900
None
69
2013
13600
None
None
(6)遍历xml文档查询三个子对象里全部实际标签包括的内容

for child in root:
     for i in child:
         print(i.tag, i.text)

查询结果:
rank 2
year 2010
gdppc 141100
neighbor None
neighbor None
rank 5
year 2013
gdppc 59900
neighbor None
rank 69
year 2013
gdppc 13600
neighbor None
neighbor None
(7)只遍历year 节点

for node in root.iter('year'):
     print(node.tag, node.text)

查询结果:
year 2010
year 2013
year 2013
2.删除

for country in root.findall('country'):        #直接对country遍历不用双重遍历
     rank = int(country.find('rank').text)     #找到排名的值
     if rank > 50:
         root.remove(country)                  #删除country整个节点

tree.write('output.xml')

打印删除后:
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year updated="yes">2011</year>
        <gdppc>141100</gdppc>
        <neighbor direction="E" name="Austria" />
        <neighbor direction="W" name="Switzerland" />
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year updated="yes">2014</year>
        <gdppc>59900</gdppc>
        <neighbor direction="N" name="Malaysia" />
    </country>
</data>
3.建立xml文档:

import xml.etree.ElementTree as ET          #用ET这个名字代替xml.etree.ElementTree

new_xml = ET.Element("namelist")            #建立根节点标签

name = ET.SubElement(new_xml, "name", attrib={"enrolled": "yes"})  #new_xml要处理的对象,"name"插入的内容标签,"enrolled": "yes"标签属性
age = ET.SubElement(name, "age", attrib={"checked": "no"})
sex = ET.SubElement(name, "sex")
sex.text = '33'

et = ET.ElementTree(new_xml)     #生成文档对象
et.write("test.xml", encoding="utf-8", xml_declaration=True)

ET.dump(new_xml)                 #打印生成的格式

建立结果:
<?xml version='1.0' encoding='utf-8'?>
<namelist>
    <name enrolled="yes">
          <age checked="no" />
          <sex>33</sex></name>
    <name enrolled="no">
          <age>19</age></name>
</namelist>
re模块(正则表达式)
一.正则表达式概述
1.概述:正则表达式是文本处理极为重要的技术,用它能够对字符串按照某种规则进行检索,替换。
2.分类:
(1)基本正则表达式(BRE):grep,sed,vi等软件支持。vim有扩展
(2)扩展正则表达式(ERE):egrep(grep -E),sed -r等
(3)PCRE:几乎全部高级语言都是PCRE的方言或者变种。python从1.6开始使用SRE正则表达式引擎,能够认为是PCRE的子集,见模块re
二.re模块概述
就本质而言,正则表达式(或RE)是一种小型的,高度专业化的编程语言,(在python中)它内嵌在python中,并经过re模块实现。正则表达式模式被编译成一系列的字节码,而后由用C编写的匹配引擎执行
三.re模块下的字符:字符匹配包括普通字符和元字符
1.普通字符:大多数字符串和字母都会和自身匹配

>>> import re
>>> re.findall("xixi","xixishishixixiyaoyao")
###re.findall匹配:一个参数匹配规则,第二个参数要匹配的内容打印:
['xixi', 'xixi']

2.元字符
(1)通配符:.表明除换行符的任意符号,一个.表明一个符号

import re
>>> re.findall("w..g","thedwangddfegzfe")  #匹配w开头中间任意俩个字符以g结尾的
####匹配结果:
['wang']

(2)以什么开头匹配:^

import re
>>> re.findall("^t..d","thedwangddfegzfe")  
####匹配结果:
['thed']

(3)以什么结尾匹配:$

import re
>>> re.findall("g..e$","thedwangddfegzfe")  #匹配以e结尾g开头中间任意俩个字符
####匹配结果:
['gzfe']

(4)或:|

import re
>>> re.findall("wa|ng","sdwagekngc")    #匹配wa或者ng
####匹配结果:
['wa', 'ng']

(5重复匹配:*匹配0到无穷次

import re
>>> re.findall("^d*","dddddddddddddddfdafadfaddddfcaddd") #匹配以d开头的任意字符
####匹配结果:
['ddddddddddddddd']

(6)重复匹配:+匹配1到无穷次

import re
re.findall("d+","dddddddddddddddfdafadfaddddfcaddd")     #匹配以全部d
####匹配结果:
['ddddddddddddddd', 'd', 'd', 'dddd', 'ddd']

(7)匹配0和1的范围:?

import re
>>> re.findall("wang?","asdhfawanggg")   #匹配wan后面1个g
####匹配结果:
['wang']

(8)把取的范围放到大括号里:{}

import re
>>> re.findall("wang{2}","asdhfawanggg")  #匹配wan后面2个g
####匹配结果:
['wangg']

四.字符之字符集:[ ]
1.第一个功能起到或的做用[ ]

import re
>>> re.findall("w[az]","fdasfafkjdkfwajldfjwz")  #匹配以w开头,后面跟a或z
###匹配结果:
['wa', 'wz']

2.范围一个到一个匹配:[-]

import re
>>> re.findall("w[a-z]","wdkedjtksfewz") #匹配第一位w第二位是a-z的任意一个字符
###匹配结果:
['wd', 'wz']

3.非匹配:[^]

import re
>>> re.findall("w[^a-z]","w123213wqdxz")  #匹配第一位w第二位不是a-z的任意字符的一个
###匹配结果:
['w1']

五.元字符之转义符:\
斜杠后边跟元字符去除特殊功能
斜杠后边跟普通字符实现特殊功能
(1)\d匹配任何十进制数;至关于[0-9]
(2)\D匹配任何非数字字符:至关于[^[0-9]]
(3)\s匹配任空白字符:至关于[\t\n\r\f\v]
(4)\S匹配任何非空白字符:至关于[^\t\n\r\f\v]
(5)\w匹配任何字母数字字符:至关于[a-zA-Z0-9_]
(6)\W匹配任何非字母数字字符:至关于[^a-zA-Z0-9_]
(7)\b匹配一个特殊字符边界,好比空格,&,#等
举例:

import re
re.findall("I\\b","hello I am LIST")   #匹配单独的大写字母I
###匹配结果:详解:\\b 在python解释器里解释到\b的时候,他直接把\b翻译成python解释的转义,因此前面在加上\把\b翻译成对应的内容
['I']

六.re模块下的修饰符
正则表达式能够包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。
修饰符    描述
re.I     使匹配对大小写不敏感
re.L     作本地化识别(locale-aware)匹配
re.M     多行匹配,影响 ^ 和 $
re.S     使 . 匹配包括换行在内的全部字符
re.U     根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X     该标志经过给予你更灵活的格式以便你将正则表达式写得更易于理解。
七.re模块下的经常使用方法
1.编译
(1)re.compile(pattern,flags=0)
(2)设定flags,编译模式,返回正则表达式对象regex
(3)pattern就是正则表达式字符串,flags是选项。正则表达式须要被编译,为了提升效率,这些编译后的结果被保存,下次使用一样的pattern的时候,就不须要再次编译。
(4)re的其它方法为了提升效率都调用了编译方法,就是为了提速
2.单次匹配(分三种)
第一种(match):从头向后找到表达式第一次匹配子字符串,就当即返回,返回一个match对象,match里面包含从哪里开始到那里结束
(1)re.match(pattern,string,flags=0)
举例:没编译单行匹配

import re
s = '0123abc'

#没编译
matcher = re.match('\d',s)  #匹配0-9
print(type(matcher))        #匹配以后的类型:<class '_sre.SRE_Match'>
print(matcher)              #匹配的match对象:<_sre.SRE_Match object; span=(0, 1), match='0'>
print(matcher.group())      #匹配的内容:0

(2)regex.match(string[,pos[,endpos]]):match匹配从字符串的开头匹配,regex对象match方法能够重设定开始位置和结束位置。返回match对象
举例:编译单行匹配

import re
s = '0123abc'

#编译:
regex = re.compile('[ab]')  #匹配从ab开始
#编译过对象内部记录编译过的结果
matcher = regex.match(s,4)  #编译后的match能够设置位置,从第4个位置开始找
print(type(matcher))        #<class '_sre.SRE_Match'>
print(matcher)              #匹配的match对象:<_sre.SRE_Match object; span=(4, 5), match='a'>
print(matcher.group())      #匹配的内容:a

(3)编译未编译多行匹配举例

import re
s = '''bottle\nbag\nbig\napple'''

for x in enumerate(s):
    if x[0] % 8 == 0:
        print()
    print(x, end=' ')
print('\n')

#match方法:
print('--match--')
print('未编译:')
#找到一个就不找了
result = re.match('b',s)
print(1,result)

#没找到,返回None
result = re.match('a',s)        #找不到,由于match只能从开头找,离开开头就找不到
print(2,result)

#没找到,返回None
result = re.match('^a',s,re.M)  #找不到,依然从开头找,多行模式没用
print(3,result)

print('已编译:')
#编译:
regex = re.compile('a')
#没找到,返回None
result = regex.match(s)          #依然从头开始找
print(1,result)
#把索引15做为开始找
result = regex.match(s,15)
print(2,result)
##############返回结果: (0, 'b') (1, 'o') (2, 't') (3, 't') (4, 'l') (5, 'e') (6, '\n') (7, 'b') (8, 'a') (9, 'g') (10, '\n') (11, 'b') (12, 'i') (13, 'g') (14, '\n') (15, 'a') (16, 'p') (17, 'p') (18, 'l') (19, 'e') --match-- 未编译: 1 <_sre.SRE_Match object; span=(0, 1), match='b'> 2 None 3 None 已编译: 1 None 2 <_sre.SRE_Match object; span=(15, 16), match='a'>

第二种(search):匹配字符串里面的结果只要找到一个知足的就不在日后面找了
(1)re.search(pattern,string,flags=0)
举例:没编译单行匹配

import re
s = '0123abc'

#没编译
matcher = re.search('[ab]',s)  #匹配[ab]
print(type(matcher))           #匹配以后的类型:<class '_sre.SRE_Match'>
print(matcher)                 #匹配的search对象:<_sre.SRE_Match object; span=(4, 5), match='a'>
print(matcher.group())         #匹配的内容:a

(2)regex.search(string[,pos[,endpos]]);从头搜索直到第一个匹配,regex对象search方法能够重设定开始位置和结束位置,返回match对象
举例:编译单行匹配

import re
s = '0123abc'

#编译:
regex = re.compile('[ab]')  #匹配从ab开始
#编译过对象内部记录编译过的结果
matcher = regex.search(s)
print(type(matcher))        #<class '_sre.SRE_Match'>
print(matcher)              #匹配的search对象:<_sre.SRE_Match object; span=(4, 5), match='a'>
print(matcher.group())      #匹配的内容:a

(3)编译未编译多行匹配举例

import re
s = '''bottle\nbag\nbig\napple'''

for x in enumerate(s):
    if x[0] % 8 == 0:
        print()
    print(x, end=' ')
print('\n')

#search方法:
print('--search--')
print('未编译:')
#扫描找到匹配的第一个位置
result = re.search('a',s)  #apple
print(1,result)

print('已编译:')
#从b开始扫描,绕过第一b
regex = re.compile('b')
result = regex.search(s,1)  #bag
print(1,result)

#从b开头开始扫描
regex = re.compile('^b',re.M)
result = regex.search(s)   #无论是否是多行,找到就返回
print(2,result)

#从第8位b开始找
regex = re.compile('b')
result = regex.search(s,8)
print(3,result)
########返回结果: (0, 'b') (1, 'o') (2, 't') (3, 't') (4, 'l') (5, 'e') (6, '\n') (7, 'b') (8, 'a') (9, 'g') (10, '\n') (11, 'b') (12, 'i') (13, 'g') (14, '\n') (15, 'a') (16, 'p') (17, 'p') (18, 'l') (19, 'e') --search-- 未编译: 1 <_sre.SRE_Match object; span=(8, 9), match='a'> 已编译: 1 <_sre.SRE_Match object; span=(7, 8), match='b'> 2 <_sre.SRE_Match object; span=(0, 1), match='b'> 3 <_sre.SRE_Match object; span=(11, 12), match='b'>

第三种(fullmatch):整个字符串和正则表达式匹配
(1)fe.fullmatch(pattern,string,flags=0)
举例:没编译单行匹配

import re
s = '0123abc'

#没编译
matcher = re.fullmatch('\w+',s)    #匹配任何字母数字字符
print(type(matcher))               #匹配以后的类型:<class '_sre.SRE_Match'>
print(matcher)                     #匹配的search对象:<_sre.SRE_Match object; span=(0, 7), match='0123abc'>
print(matcher.group())             #匹配的内容:0123abc

(2)regex.fullmatch(string[,pos[,endpos]])
举例:编译单行匹配

import re
s = '0123abc'

#编译:
regex = re.compile('[ab]')        #匹配从ab开始
#编译过对象内部记录编译过的结果
matcher = regex.fullmatch(s,4,5)   #先作字符串切片,从第4个位置开始找
print(type(matcher))               #<class '_sre.SRE_Match'>
print(matcher)                     #匹配的search对象:<_sre.SRE_Match object; span=(4, 5), match='a'>
print(matcher.group())             #匹配的内容:a

(3)编译未编译多行匹配举例

import re
s = '''bottle\nbag\nbig\napple'''

for x in enumerate(s):
    if x[0] % 8 == 0:
        print()
    print(x, end=' ')
print('\n')

#fullmatch方法:
print('--fullmatch--')
print('未编译:')
#匹配bag
result = re.fullmatch('bag',s)
print(1,result)

print('已编译:')
#编译:
regex = re.compile('bag')
#没匹配上
result = regex.fullmatch(s)      #匹配bag
print(1,result)

#没匹配上
result = regex.fullmatch(s,7)    #匹配bag从第七位开始
print(2,result)

#要彻底匹配,多了少了都不行
result = regex.fullmatch(s,7,10)  #匹配bag从第七位开始到第十位
print(3,result)
########返回结果: (0, 'b') (1, 'o') (2, 't') (3, 't') (4, 'l') (5, 'e') (6, '\n') (7, 'b') (8, 'a') (9, 'g') (10, '\n') (11, 'b') (12, 'i') (13, 'g') (14, '\n') (15, 'a') (16, 'p') (17, 'p') (18, 'l') (19, 'e') --fullmatch-- 未编译: 1 None 已编译: 1 None 2 None 3 <_sre.SRE_Match object; span=(7, 10), match='bag'>

3.所有匹配(分两种)
第一种findall:对整个字符串,从左至右匹配,返回全部的匹配项列表
(1)re.findall(pattern,string,flags=0)

import re
s = '0123abc'

#没编译
matcher = re.findall('[ab]',s)  #匹配[ab]
print(type(matcher))            #匹配以后的类型:<class 'list'>
print(matcher)                  #匹配的match结果:['a', 'b']

(2)regex.findall(string[,pos[,endpos]])

import re
s = '0123abc'

#编译:
regex = re.compile('[ab]')
matcher = regex.findall(s)
print(type(matcher))        #匹配以后的类型:<class 'list'>
print(matcher)              #匹配的match结果:['a', 'b']

(3)编译未编译多行匹配举例

import re
s = '''bottle\nbag\nbig\napple'''

for x in enumerate(s):
    if x[0] % 8 == 0:
        print()
    print(x, end=' ')
print('\n')

#findall方法:
print('--findall--')
print('未编译:')
result = re.findall('b',s)      #匹配全部b
print(1,result)

print('已编译:')
#编译:
#只能找到一个bottle,默认状况\n不会看成行首
regex = re.compile('^b')        #匹配b开头的全部
result = regex.findall(s)
print(1,result)

#编译:
regex = re.compile(r'^b\w+',re.M)   #匹配b开头每一行的行首
result = regex.findall(s,7)
print(2,result)

#编译:
regex = re.compile(r'^b\w+',re.S)
result = regex.findall(s)
print(3,result)

#编译:
regex = re.compile(r'^b\w+',re.M)
result = regex.findall(s,7,10)
print(4,result)
########返回结果: (0, 'b') (1, 'o') (2, 't') (3, 't') (4, 'l') (5, 'e') (6, '\n') (7, 'b') (8, 'a') (9, 'g') (10, '\n') (11, 'b') (12, 'i') (13, 'g') (14, '\n') (15, 'a') (16, 'p') (17, 'p') (18, 'l') (19, 'e') --findall-- 未编译: 1 ['b', 'b', 'b'] 已编译: 1 ['b'] 2 ['bag', 'big'] 3 ['bottle'] 4 ['bag']

第二种finditer:对整个字符串,从左至右匹配,返回全部匹配项,返回迭代器。
注意:每次迭代返回的是match对象。
(1)re.finditer(pattern,string,flags=0)

import re
s = '0123abc'

#没编译
matcher = re.finditer('[ab]',s)  #匹配[ab]
print(type(matcher))             #匹配以后的类型:<class 'list'>
print(matcher)                   #匹配的match对象:<callable_iterator object at 0x00000000011C14A8>
print(next(matcher).group())     #返回结果:a
print(next(matcher).group())     #返回结果:b

(2)regex.finditer(string[,pos[,endpos]])

import re
s = '0123abc'

#编译:
regex = re.compile('[ab]')
matcher = regex.finditer(s)
print(type(matcher))             #匹配以后的类型:<class 'list'>
print(matcher)                   #匹配的match对象:<callable_iterator object at 0x00000000011C14A8>
print(next(matcher).group())     #返回结果:a
print(next(matcher).group())     #返回结果:b

4.匹配替换
第一种sub:使用pattern对字符串string进行匹配,对匹配项使用repl替换replacement能够是string,bytes,function
(1)re.sub(pattern,replacement,string,count=0,flags=0)
(2)regex.sub(replacement,string,count=0)
举例:

import re
s = '''bottle\nbag\nbig\napple'''

for x in enumerate(s):
    if x[0] % 8 == 0:
        print()
    print(x, end=' ')
print('\n')

#编译:
regex =re.compile('b\wg')       #匹配b中间加一个字符的
#
result = regex.sub('xixi',s)    #替换成xixi
print(1,result)                  #被替换后的字符串
#
result = regex.sub('xixi',s,1)  #替换成xixi只替换一次
print(2,result)
######打印结果 (0, 'b') (1, 'o') (2, 't') (3, 't') (4, 'l') (5, 'e') (6, '\n') (7, 'b') (8, 'a') (9, 'g') (10, '\n') (11, 'b') (12, 'i') (13, 'g') (14, '\n') (15, 'a') (16, 'p') (17, 'p') (18, 'l') (19, 'e') 1 bottle xixi xixi apple 2 bottle xixi big apple

第二种subn:同sub返回一个元祖(new_string[替换后的字符串],number_of_subs_made[替换的次数])
(1)re.subn(pattern,replacement,string,count=0,flags=0)
(2)regex.subn(replacement,string,count=0)
举例:

import re
s = '''bottle\nbag\nbig\napple'''

for x in enumerate(s):
    if x[0] % 8 == 0:
        print()
    print(x, end=' ')
print('\n')

#编译:
regex =re.compile('b\wg')       #匹配b中间加一个字符的
#
result = regex.subn('xixi',s)    #替换成xixi
print(1,result)                  #被替换后的字符串
#
result = regex.subn('xixi',s,1)  #替换成xixi只替换一次
print(2,result)

######打印结果
(0, 'b') (1, 'o') (2, 't') (3, 't') (4, 'l') (5, 'e') (6, '\n') (7, 'b')
(8, 'a') (9, 'g') (10, '\n') (11, 'b') (12, 'i') (13, 'g') (14, '\n') (15, 'a')
(16, 'p') (17, 'p') (18, 'l') (19, 'e')

1 ('bottle\nxixi\nxixi\napple', 2)
2 ('bottle\nxixi\nbig\napple', 1)

举例2

import re
s = '''os.path([path])'''

##编译:
regex =re.compile('[\.\(\)\[\]]')       #把.和小括号和中括号替换
#
result = regex.subn(' ',s)    #替换成空格
print(result)                 #被替换后的字符串

#######打印结果: ('os path path ', 5)

5.字符串分割
re.split(pattern,string,maxsplit=0,flags=0):
举例:

import re
s = '''01 bottle
02 bag
03    big1
100     able'''

for x in enumerate(s):
    if x[0] % 8 == 0:
        print()
    print(x, end=' ')
print('\n')

#把每行单词提取出来
#方式一:
regex = re.compile('^[\s\d]+',re.M)    #匹配任空白字符或0-9开头
result = regex.split(s)                 #分割
print(result)
#方式二:
regex = re.compile('\s+\d+\s+')      #匹配任空左右两边是白字符中间是0-9开头
result = regex.split(' '+s)           #分割切掉,把01前面加上空格
print(result)
#######打印结果: (0, '0') (1, '1') (2, ' ') (3, 'b') (4, 'o') (5, 't') (6, 't') (7, 'l') (8, 'e') (9, '\n') (10, '0') (11, '2') (12, ' ') (13, 'b') (14, 'a') (15, 'g') (16, '\n') (17, '0') (18, '3') (19, ' ') (20, ' ') (21, ' ') (22, ' ') (23, 'b') (24, 'i') (25, 'g') (26, '1') (27, '\n') (28, '1') (29, '0') (30, '0') (31, ' ') (32, ' ') (33, ' ') (34, ' ') (35, ' ') (36, 'a') (37, 'b') (38, 'l') (39, 'e') ['', 'bottle\n', 'bag\n', 'big1\n', 'able'] ['', 'bottle', 'bag', 'big1', 'able']

6.分组
使用小括号的pattern捕获的数据被放到了组group中
match,search函数能够返回match对象;findall返回字符串列表;finditer返回一个个match对象
若是pattern中使用了分组,若是有匹配的结果,会在match对象中
(1)使用group(N)方式返回对应分组,1-N是对应的分组,0返回整个匹配的字符串
(2)若是使用了命名分组,可使用group('name')的方式取分组
(3)也可使用groups()返回全部组
(4)使用groupdict()返回全部命名的分组
举例:

import re
s = '''bottle\nbag\nbig\napple'''

#search方式分组
print('--search分组--')
regex = re.compile('b(\w+)(e)')  #匹配b开头e结尾中间是字母
matcher = regex.search(s)         #把中间字母和结尾e分两组
print(matcher.groups())           #返回全部分组元祖打印:('ottl', 'e')
print(matcher.group(0))           #全长匹配mtch对象打印:bottle
print(matcher.group(1))           #打印第一个分组:ottl
print(matcher.group(2))           #打印第二个分组:e

#finditer方式分组
print('--finditer分组--')
regex = re.compile('b(\w+)(e)')  #匹配b开头e结尾中间是字母
matchers = regex.finditer(s)      #把中间字母和结尾e分两组
for matcher in matchers:
    #分组元祖打印
    print(matcher.groups())       #返回全部分组元祖打印:('ottl', 'e')
    print(matcher.group(0))       #全长匹配mtch对象打印:bottle
    print(matcher.group(1))       #打印第一个分组:ottl
    print(matcher.group(2))       #打印第二个分组:e

#finditer方式命名分组
print('--finditer命名分组--')
regex = re.compile('b(?P<body>\w+)(?P<tail>e)')  #匹配b开头e结尾中间是字母,把开头b命名为body,把结尾e命名为tail
matchers = regex.finditer(s)                      #把中间字母和结尾e分两组
for matcher in matchers:
    #分组名字字典方式打印
    print(matcher.groupdict())                    #打印分组名字:{'tail': 'e', 'body': 'ottl'}

logging模块(日志模块)
1.日志级别(CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET)

 日志级别     数值
CRITICAL     50
ERROR        40
WARNING      30(默认级别)
INFO         20
DEBUG        10
NOTSET       0

日志级别指的是生产日志的事件的严重程度。设置一个级别后,严重程度低于设置值的日志消息将被忽略
2.格式字符串:

      属性名                    格式                                        描述
    日志名字                %(name)s:            logger的名字
    日志消息内容:           %(message)s           The logged message,computed as msg % args.当调用Formatter.format()时设置
    asctime                %(asctime)s          字符串形式的当前时间。默认格式是"2018-01-01 11:11:11,11"。逗号后面的是毫秒
    created                %(created)f          当前时间,用UNIX标准的表示时间的浮点数表示
    输出日志信息             %(relativeCreated)d  输出日志信息时的,自Logger建立以来的毫秒数
    函数名                  %(funcName)s         调用日志输出函数的函数名
    日志级别名称             %(levelname)s        文本形式的日志级别
    日记级别数值             %(levelno)s          数字形式的日志级别
    行号                   %(lineno)d            调用日志输出函数的语句所在的代码行
    模块                   %(module)s            调用日子输出函数的模块名
    模块的完整路径名         %(pathname)s          调用日志输出函数的模块的完整路径名
    模块文件名              %(filename)s          调用日志输出函数的模块文件名
    进程ID                 %(process)d           进程ID
    线程ID                 %(thread)d            线程ID
    进程名                 %(processName)s       进程名
    线程名                 %(threadName)s        线程名

3.可用参数:
(1)level设置rootlogger的日志级别(默认大于等于warning级别)
(2)filename:用指定的文件名建立,这样日志会被存储在指定的文件中
(3)filemode:文件打开方式,在指定文件时使用这个参数,默认值为"a",还可指定为"w"
(4)datefmt:指定日期时间格式
(5)stream:是指定的stream建立StreamHandler,若同时出现filename和stream俩个参数,stream则被忽略
(6)format:指定handler(处理的日志)使用的日志显示格式   
<1>默认级别

import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

####打印结果:默认的级别是warning,只会把warning以上的都打印出来
WARNING:root:warning message
ERROR:root:error message
CRITICAL:root:critical message

<2>修改日期格式

import logging

#指定日志打印的格式
FORMAT = "%(asctime)s %(thread)d %(message)s"
#datefmt="%Y-%m-%d-%H:%M:%S"修改日期格式
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S")
logging.info('info message')                          #打印结果:2019-10-24-17:08:28 77460 info message

<3>构建消息两种风格(C语言风格和format函数风格)

import logging

#指定日志打印的格式必须是C语言风格
FORMAT = "%(asctime)s %(thread)d %(message)s"

logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S")

def add(x,y):
    #INFO
    #风格一:C语言风格把("%s %s",x,y)放到message里
    logging.info("%s %s", x, y)

    #WARNING
    #风格二:format函数合成一个字符串放到message里
    logging.warning("{} {}".format(threading.enumerate(),x+y))

add(4,5)
###返回结果:
2019-10-24-15:31:07 76444 4 5
2019-10-24-15:31:07 76444 [<_MainThread(MainThread, started 76444)>] 9

总结:上例是基本使用方法,大多数时候,使用的是info,正常运行信息的输出
<4>自定义格式字符串扩展

import logging

#指定日志打印的格式必须是C语言风格(%(age)s是额外本身定义的信息)
FORMAT = "%(asctime)s %(thread)d %(message)s %(age)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S")

#必须把额外的信息放到字典里
d = {'age' : '18'}

def add(x,y):
    #WARNING
    #经过extra把这个字典调出来
    logging.warning("{} {}".format(threading.enumerate(),x+y) , extra=d)

add(4,5)

##打印结果:
2019-10-24-15:47:13 67708 [<_MainThread(MainThread, started 67708)>] 9 18

<5>filename输入到文件

import logging

#指定日志打印的格式必须是C语言风格
FORMAT = "%(asctime)s %(thread)d %(message)s"
#(写入文件filename=E:/aaa/logger.log)
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S",filename='E:/aaa/logger.log')

def add(x,y):
    #WARNING
    #经过extra把这个字典调出来
    logging.warning("{} {}".format(threading.enumerate(),x+y))

add(4,5)
##查看E:/aaa/logger.log文件内容
2019-10-24-15:52:29 71712 [<_MainThread(MainThread, started 71712)>] 9

4.Logger类
(1)构造:使用工厂方法返回一个Logger实例
语法:logging.getLogger([name=None])
①指定name,返回一个名称为name的Logger实例。若是再次使用相同的名字,是实例化一个对象。未指定name,返回Logger实例,名称是root,即根Logger。
②Logger是层次结构的,使用.点号分割,如'a','a.b'或'a.b.c.d',a是a.b的父parent,a.b是a的子child。对于foo来讲,名字为foo.bar,foo.bar.baz,foo.bam都是foo的后代
层级关系:

import logging

#指定日志打印的格式必须是C语言风格
FORMAT = "%(asctime)s %(thread)d %(message)s"
#WARNING
logging.basicConfig(level=logging.WARNING,format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S")

#父亲(根logger)
root = logging.getLogger()
print(root.name,root, id(root))
root.warning('my root')

#root的子(__name__模块级的)
loga = logging.getLogger('__name__')
print(loga.name,loga, id(loga),id(loga.parent))
loga.warning('my loga')

#loga的子(__name__,模块下的某个函数'echo'。或某个类产生的日志)
logab = logging.getLogger("{}.{}".format(__name__,'echo'))
print(logab.name,logab,id(logab),id(logab.parent))
logab.warning('my logab')
###打印结果:
2019-10-24-16:42:40 75980 my root
root <logging.RootLogger object at 0x0000000000E2C240> 14860864
2019-10-24-16:42:40 75980 my loga
__name__ <logging.Logger object at 0x0000000000E05550> 14701904 14860864
2019-10-24-16:42:40 75980 my logab
__main__.echo <logging.Logger object at 0x0000000000E2C400> 14861312 14860864

5.级别设置setLevel

import logging

FORMAT = "%(asctime)s %(thread)d %(message)s"
#INFO
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S")
#根的一个子logger
logger = logging.getLogger('__name__')
#日志级别继承根
print(logger.getEffectiveLevel())        #打印结果:20
#打印info级别的日志
logger.info('hello1')                   #打印结果:2019-10-24-16:53:55 71648 hello1
#修改日志级别
logger.setLevel(28)
#打印当前日志级别
print(logger.getEffectiveLevel())        #打印结果:28
#修改后打印info级别的日志就打印不出来了
logger.info('hello2')

6.Handler:控制日志信息的输出目的地,能够是控制台,文
①能够单独设置level
②能够单独设置格式
③能够单独设置过滤器

● Handler
    ○ StreamHandler        #不指定使用sys.stderr
        ■ FileHandler      #文件
        ■ _StderrHandler   #标准输出
    ○ NullHandler          #什么都不作

举例:logger实例级别(level)继承(没有设置任何的handler,leverl)

import logging

FORMAT = "%(asctime)s %(thread)d %(message)s"
#基本设置INFO
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S")

#拿到一个root
root = logging.getLogger()

##子
#使用工厂方法返回一个Logger实例
log1 = logging.getLogger('s')
#继承祖先info级别的日志
log1.info('hello1')                   #打印结果:2019-10-24-17:42:01 73348 hello1
#修改日志级别ERROR
log1.setLevel(logging.ERROR)
print(log1.getEffectiveLevel())        #打印结果:40

##孙(没有设置任何的handler,leverl)
log2 = logging.getLogger('s.s1')
#log2继承最进的祖先有效级别log1的ERROR打印
print(log2.getEffectiveLevel())        #打印结果:40
#因此warning打印不出来
log2.warning('log2 warning')

总结:logger实例,若是设置了level,就用它和信息的级别比较,不然,继承最近的祖先level。
(1)能够单独设置level

import logging

#指定日志打印的格式必须是C语言风格
FORMAT = "%(asctime)s %(thread)d %(message)s"
#INFO
logging.basicConfig(format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S",level=logging.INFO)

##############################################################
###子
#使用工厂方法返回一个Logger实例
log1 = logging.getLogger('s')
#修改日志级别WARNING
log1.setLevel(logging.ERROR)

##给log1加一个Handler写入文件
hdlr = logging.FileHandler('E:/wangshiyao/aaa/m/log1hdlr.log')
#Handler日志级别WARNING
hdlr.setLevel(logging.WARNING)
#在log1里填进去一个Handler,同时这个Handler的级别(WARNING)跟本身级别(ERROR)不同
log1.addHandler(hdlr)               #接收log3(孙子)的级别判断是否大于等于log2(父)给Handler处理把log3写入到文件里
#打印log1的handlers
print('log1',log1.handlers)        #打印结果:
#################################################################
###孙(设置handler)
log2 = logging.getLogger('s.s1')
#修改日志级别CRITICAL
log1.setLevel(logging.CRITICAL)

##给log2加一个Handler标准输出
hdlr2 = logging.StreamHandler()
#Handler日志级别WARNING
hdlr2.setLevel(logging.WARNING)
#在log2里填进去一个Handler,同时这个Handler的级别(WARNING)跟本身级别(CRITICAL)不同
log2.addHandler(hdlr2)               #接收log3(子)的级别判断是否大于等于log2(父)给Handler处理打印输出:log3
#打印log2的handlers
print('log2',log2.handlers)         #打印结果:
#################################################################
###重孙
log3 = logging.getLogger('s.s1.s2')
#设置log3的有效级别为INFO
log3.setLevel((logging.INFO))
#打印log3的有效级别
print(log3.getEffectiveLevel())      #打印结果:20
#log3定义的级别INFO低于warning
log3.warning('log3')                #返回结果:2019-10-28-17:26:23 10524 log3
#打印log3的handlers
print('log3',log3.handlers)         #打印结果:log3 []

总结:
①每个Logger实例的level如同入口,让水溜进来,若是这个门槛过高,信息就进不来。例如log3.warning(log3),若是定义的级别(INFO)高于于warning,就不会有信息经过log3
②若是level没有设置,就用父的logger的,若是父亲logger的level没有设置,继续找父亲的父亲,最终能够找到root上,若是root设置了就用它的,若是root没有设置就用默认的WARNING
③在某个logger上产生某种级别的消息,首先和logger的level检查,若是消息level低于logger的EffectiveLevel有效级别,消息丢弃。若是经过(大于等于)检查后,消息交给logger全部的handler处理,每一个handler须要和本身level比较来决定是否处理。若是没有一个handler,或者消息已经被handler处理过了,则须要经过本logger的propagate属性是不是True,True则把这个消息会继续发送给父logger的全部handler处理,若是propagate属性是True,当前logger的父logger成为当前logger,它的全部handler处理消息。
④logger实例初始的propagate属性为True,即容许向父logger传递消息
⑤logging.basicConfig若是root没有handler,就默认建立一个StreamHandler,若是设置了filename,就建立一个FileHandler。若是设置了format参数,就会用它生成了一个formatter对象,并把这个formatter加入到刚才建立的handler上,而后把这些handler加入到root.handlers列表上。level是设置给root logger的。若是root.handlers列表不为空,logging.basicConfig的调用什么都不作
(2)Formatter能够单独设置格式:loggingd的Formatter类,它容许指定某个格式的字符串。若是提供None,那么'%(message)s'将会做为默认值。

import logging

#指定日志打印的格式必须是C语言风格
FORMAT = "%(asctime)s %(thread)d %(message)s"
#INFO
logging.basicConfig(level=logging.ERROR,format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S")

###子
#使用工厂方法返回一个Logger实例
log1 = logging.getLogger('s')
#修改日志级别WARNING
log1.setLevel(logging.WARNING)
#打印log1的有效级别
print(log1.getEffectiveLevel())        #打印结果:30

#给log1加一个Handler写入文件
hdlr = logging.FileHandler('E:/wangshiyao/aaa/m/log1hdlr.log')
#Handler日志级别INFO
hdlr.setLevel(logging.INFO)
#定一个Formatter指定格式
fmt1 = logging.Formatter('log1-h %(asctime)s %(thread)d %(threadName)s %(message)s')
#给Handler加一个Formatter
hdlr.setFormatter(fmt1)
#在log1里填进去一个Handler,同时这个Handler的级别(WARNING)跟本身级别(INFO)不同
log1.addHandler(hdlr)               #接收log2(子)的级别消息大于log1(父)给Handler处理把Formatter指定的格式信息log1-h 2019-10-28 14:31:37,300 10556 MainThread log2.error写入到文件里

###孙
log2 = logging.getLogger('s.s1')
#打印log2的有效级别继承父(log1)的级别WARNING
print(log2.getEffectiveLevel())      #打印结果:30

#给log2加一个Handler标准输出
hdlr2 = logging.StreamHandler()
#Handler日志级别ERROR
hdlr2.setLevel(logging.ERROR)
#定一个Formatter指定格式
fmt2 = logging.Formatter('log2-h %(asctime)s %(thread)d %(threadName)s %(message)s')
#给Handler加一个Formatter
hdlr2.setFormatter(fmt2)
#在log2里填进去一个Handler,同时这个Handler的级别(ERROR)跟本身级别(WARNING)不同
log2.addHandler(hdlr2)              #log2的log2.error级别消息大于等于本身的级别Formatter指定的格式信息标准输出:log2-h 2019-10-28 14:32:54,945 11120 MainThread log2.error
#打印log2.warning不能够打印出来
log2.info('log2 warning')         #打印结果:无
#打印log2.error能够打印出来
log2.error('log2.error')          #打印结果:2019-10-28-12:18:44 11240 log2.error

(3)Filter能够单独设置过滤器:能够为handler增长过滤器,因此过滤器只影响某一个handler,不会影响整个处理流程

import logging

#指定日志打印的格式必须是C语言风格
FORMAT = "%(asctime)s %(thread)d %(message)s"
#INFO
logging.basicConfig(level=logging.ERROR,format=FORMAT,datefmt="%Y-%m-%d-%H:%M:%S")

###子
#使用工厂方法返回一个Logger实例
log1 = logging.getLogger('s')
#修改日志级别WARNING
log1.setLevel(logging.WARNING)
#打印log1的有效级别
print(log1.getEffectiveLevel())        #打印结果:30

#给log1加一个Handler写入文件
hdlr = logging.FileHandler('E:/wangshiyao/aaa/m/log1hdlr.log')
#Handler日志级别INFO
hdlr.setLevel(logging.INFO)
#在log1里填进去一个Handler,同时这个Handler的级别(WARNING)跟本身级别(INFO)不同
log1.addHandler(hdlr)               #接收log2(子)的级别消息大于log1(父)给Handler处理把Formatter指定的格式信息log1-h 2019-10-28 14:31:37,300 10556 MainThread log2.error写入到文件里
#设置Filter过滤s
f1 = logging.Filter('s')           #s或s.s1是能够过滤的到的,因此正常写入文件
#把Filter加到Handler
hdlr.addFilter(f1)

###孙
log2 = logging.getLogger('s.s1')
#打印log2的有效级别继承父(log1)的级别WARNING
print(log2.getEffectiveLevel())      #打印结果:30

#给log2加一个Handler标准输出
hdlr2 = logging.StreamHandler()
#Handler日志级别ERROR
hdlr2.setLevel(logging.ERROR)
#过滤器s.s2不符合s和s.s1因此
f2 = logging.Filter('s.s2')
hdlr2.addFilter(f2)
#在log2里填进去一个Handler,同时这个Handler的级别(ERROR)跟本身级别(WARNING)不同
log2.addHandler(hdlr2)              #过滤器s.s2不符合s.s1或s因此没有标准显示输出
#打印log2.warning不能够打印出来
log2.info('log2 warning')         #打印结果:无
#打印log2.error能够打印出来
log2.error('log2.error')          #打印结果:2019-10-28-12:18:44 11240 log2.error

configparser模块(配置文件操做)
一.读方法:
1.read(filenames,encoding=None):读取ini文件,能够是单个文件,也能够是文件列表。能够指定文件编码。
(1)sections()返回section列表。缺省section不包括在内。
(2)add_section(section_name)增长一个section。
(3)has_section(section_name)判断section是否存在
(4)options(section)返回section的全部option
(5)has_option(section,option)判断section是否存在这个option
2.get(section,option,*raw=False,vars=None[,fallback]):从指定的段的选项上取值,若是找到返回,若是没有找到就去找 DEFAULT段有没有
(1)getint(section,option,,raw=False,vars=None[,fallback])
(2)getfoat(section,option,raw=False,vars=None[,fallback])
(3)getboolean(section,option,*,raw=False,vars=None[,fallback])
上面3个方法和get同样,返回指定类型数据
举例:读取配置文件,用get和getint方式分别读取sections里的值
<1>E:/wangshiyao/aaa/mysql.ini配置文件:

[DEFAULT]
a = test

[mysql]
default-character-set = utf8

[mysqld]
datadir = /dbserver/data
port = 3306
charcter-set-server = utf8
sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

[test]
test1 = 123
test2 = abc

<2>代码:

from configparser import ConfigParser

#打开阅读这个文件
cfg = ConfigParser()
cfg.read('E:/wangshiyao/aaa/mysql.ini')
print(cfg.sections())                         #看有哪些sections:['mysql', 'mysqld', 'test']

#读取test名字的sections的其中一条test对应的值不作强制类型转换
a = cfg.get('test','test1')
print(a,type(a))                              #123 <class 'str'>

#读取test名字的sections的其中一条test对应的值作强制类型转换成int能够直接作运算
b = cfg.getint('test','test1')
print(b,type(b))                              #123 <class 'int'>

#读取test名字的sections若是test没有对应的a值,读取[DEFAULT]里a的值([DEFAULT]必须有a)
c = cfg.get('test','a')
print(c,type(c))                              #test <class 'str'>
#打印:
['mysql', 'mysqld', 'test']
123 <class 'str'>
123 <class 'int'>
test <class 'str'>

3.items(section,raw=False,vars=None):没有section,则返回全部section名字及其对象;若是指定section,则返回这个section的键值对组成二元组
二.写方法:
1.set(section,option,value):section存在的状况下,写入option=value,要求option,value必须是字符串
举例:读取配置文件,打印出全部的sections对应的option,增长一个sections为test,并给test添加两条数据
<1>E:/wangshiyao/aaa/mysql.ini配置文件:
[

DEFAULT]
a = test

[mysql]
default-character-set = utf8

[mysqld]
datadir = /dbserver/data
port = 3306
charcter-set-server = utf8
sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

<2>代码:

from configparser import ConfigParser

#打开阅读这个文件
cfg = ConfigParser()
cfg.read('E:/wangshiyao/aaa/mysql.ini')
print(cfg.sections())                         #看有哪些sections:['mysql', 'mysqld']

#迭代cfg.sections()拿到全部sections
for section in cfg.sections():
    #迭代全部sections获取对应的option
    for k,v in cfg.items(section):
        print(k,v)

#建立一个sections
if not cfg.has_section('test'):        #判断若是有没有test名字的sections
    cfg.add_section('test')             #建立一个test名字的sections

#给test名字的sections增长两条值
cfg.set('test','test1','123')
cfg.set('test','test2','abc')

#把值写入到文件
with open('E:/wangshiyao/aaa/mysql.ini','w') as f:
    cfg.write(f)
###打印输出: ['mysql', 'mysqld', 'test'] a test default-character-set utf8 a test datadir /dbserver/data port 3306 charcter-set-server utf8 sql_mode NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES a test

<3>E:/wangshiyao/aaa/mysql.ini最新配置文件:

[DEFAULT]
a = test

[mysql]
default-character-set = utf8

[mysqld]
datadir = /dbserver/data
port = 3306
charcter-set-server = utf8
sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

[test]
test1 = 123
test2 = abc

三.删除方法:
1.remove_section(section):移除section及其全部option
2.remove_option(section,option):移除section下的option
举例:
<1>E:/wangshiyao/aaa/mysql.ini最新配置文件:

[DEFAULT]
a = test

[mysql]
default-character-set = utf8

[mysqld]
datadir = /dbserver/data
port = 3306
charcter-set-server = utf8
sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

[test]
test1 = 123
test2 = abc

<2>代码:

from configparser import ConfigParser

#打开阅读这个文件
cfg = ConfigParser()
cfg.read('E:/wangshiyao/aaa/mysql.ini')
#print(cfg.sections())                         #看有哪些sections:['mysql', 'mysqld', 'test']

#打印test名字的sections的其中test1
print(cfg.getint('test','test1'))

#删给test名字的sections的其中test1(若是没有报错)
cfg.remove_option('test','test1')

#把修改结果写入到文件
with open('E:/wangshiyao/aaa/mysql.ini','w') as f:
   cfg.write(f)
#返回:
123

四.拷贝方法:
1.write(fileobject,space_around_delimiters=True):将当前config的全部内容写入fileobject中,通常open函数使用w模式
举例:将配置文件mysql.ini拷贝一份到mysql_new.ini

from configparser import ConfigParser

#打开阅读这个文件
cfg = ConfigParser()
cfg.read('E:/wangshiyao/aaa/mysql.ini')

with open('E:/wangshiyao/aaa/mysql_new.ini','w') as f:
    cfg.write(f)

hashlib模块(摘要算法)
把一个不定长的字符串转成定长的密文的内容
用hashlib的md5算法加密数据
(1)普通加密:

import hashlib
obj=hashlib.md5()                     #md5对象,md5不能反解,可是加密是固定的,就是关系是一一对应,因此有缺陷,能够被对撞出来
obj.update("hello".encode("utf8"))    #要对哪一个字符串进行加密,就放这里
print(obj.hexdigest())                #拿到加密字符串

加密输出:
5d41402abc4b2a76b9719d911017c592
(2)加密加盐:

import hashlib
obj=hashlib.md5("xixi".encode('utf8'))  #在原先加密的基础上再加密一层"xixi",这样的话参数"xixi"只有本身知道,防止被撞库,由于别人永远拿不到这个参数
obj.update("hello".encode("utf8"))
print(obj.hexdigest())

加盐加密输出:
7beec303f0442fd5cf2961a56c277d96