python3-常用模块

目录

  • 认识模块
    • 什么是模块
    • 模块的导入和使用
  • 常用模块一
    • collections模块
    • 时间模块
    • random模块
    • os模块
    • sys模块
    • 序列化模块
    • re模块
  • 常用模块二
    • hashlib模块
    • configparse模块
    • logging模块

 

认识模块

什么是模块

常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀

但其实import加载的模块分为四个通用类别

  1. 使用python编写的代码(.py文件)
  2. 已被编译为共享库或DLL的C或C++扩展
  3. 包好一组模块的包
  4. 使用C编写并链接到python解释器的内置模块

为什么要使用模块?

   如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。

    随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用,

collections模块

在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。

  1. namedtuple:生成可以使用名字来访问元素内容的tuple
  2. deque:双端队列,可以快速的从另外一侧追加和推出对象
  3. Counter:计算器,主要用来计数
  4. OrderedDict:有序字典
  5. defaultdict:带有默认值的字典

namedtuple

我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:

>>> p = (1, 2)

但是,看到(1,2)很难看出这个tuple是用来表示一个坐标的。

这时,namedtuple就派上用场了:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p.x
1
>>> p.y
2

类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:

# namedtuple('名称',[属性list])
Circle = nametuple('Circle', ['x','y','r'])

deque

使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。

deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:

>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])

deque除了实现list的append()和pop外,还支持appendleft()和popleft(),这样就可以非常非常高效地往头部添加或者删除元素了。

OrderedDict

使用dict时,Key是无序的。在dict做迭代时,我们无法确定Key的顺序。

如果要保持Key的顺序,可以用OrderedDict:

>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])

注意,OrderedDict的Key会按照插入的顺序排序, 不是Key本身排序

defaultdict

有如下值集合 [11,22,33,44,55,66,77,88,99,90...],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。

即: {'k1': 大于66 'k2': 小于66}
values = [11, 22, 33,44,55,66,77,88,99,90]

my_dict = {}

for value in  values:
    if value>66:
        if my_dict.has_key('k1'):
            my_dict['k1'].append(value)
        else:
            my_dict['k1'] = [value]
    else:
        if my_dict.has_key('k2'):
            my_dict['k2'].append(value)
        else:
            my_dict['k2'] = [value]
原生字典解决办法
from collections import defaultdict

values = [11, 22, 33,44,55,66,77,88,99,90]

my_dict = defaultdict(list)

for value in  values:
    if value>66:
        my_dict['k1'].append(value)
    else:
        my_dict['k2'].append(value)
defaultdict字典解决办法

使用dict时,如果append的时候引用的key不存在,就会抛出keyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict:

>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回默认值
'N/A'

Counter

Counter类的目的是用啦跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。

c = Counter('abcdeabcdabcaba')
print c
输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})

 

时间模块

表示时间的三种方式

在Python中,通常有这三种方式来表示时间:时间戳、元组(struct_time)、格式化的时间字符串

(1)时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的便宜量。   type(time.time()), 返回的是float类型。

(2)格式化的时间字符串(Format String):‘1999-12-06’

%y 两位数的年份表示(00-99%Y 四位数的年份表示(000-9999%m 月份(01-12%d 月内中的一天(0-31%H 24小时制小时数(0-23%I 12小时制小时数(01-12%M 分钟数(00=59%S 秒(00-59%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身
Python中时间日期格式化符号:

(3)元组(struct_time):struct_time元组共有9个元素(年、月、日、时、分、秒, 一年中第几周, 一年中第几天)

索引(Index)属性(Attribute)值(Values)
0 tm_year(年) 比如2011
1 tm_mon(月) 1 - 12
2 tm_mday(日) 1 - 31
3 tm_hour(时) 0 - 23
4 tm_min(分) 0 - 59
5 tm_sec(秒) 0 - 60
6 tm_wday(weekday) 0 - 6(0表示周一)
7 tm_yday(一年中的第几天) 1 - 366
8 tm_isdst(是否是夏令时) 默认为0

首先,我们先导入time模块,来认识一下python中表示时间的几种格式:

#导入时间模块
>>>import time

#时间戳
>>>time.time()
1500875844.800804

#时间字符串
>>>time.strftime("%Y-%m-%d %X")
'2017-07-24 13:54:37'
>>>time.strftime("%Y-%m-%d %H-%M-%S")
'2017-07-24 13-55-04'

#时间元组:localtime将一个时间戳转换为当前时区的struct_time
time.localtime()
>>>time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24,
          tm_hour=13, tm_min=59, tm_sec=37, 
                 tm_wday=0, tm_yday=205, tm_isdst=0)

小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的。

几种格式之间的转换

#时间戳-->结构化时间  timestamp --> struct_time(localtime, gmtime)
#time.gmtime(时间戳)    #UTC时间,与英国伦敦当地时间一致
#time.localtime(时间戳) #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间 
>>>time.gmtime(1500000000)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)
>>>time.localtime(1500000000)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)

#结构化时间-->时间戳  struct_time --> timestamp(mktime)
#time.mktime(结构化时间)
>>>time_tuple = time.localtime(1500000000)
>>>time.mktime(time_tuple)
1500000000.0
#结构化时间-->字符串时间   struct_time --> format_string (strftime)
#time.strftime("格式定义","结构化时间")  结构化时间参数若不传,则显示当前时间
>>>time.strftime("%Y-%m-%d %X")
'2017-07-24 14:55:36'
>>>time.strftime("%Y-%m-%d",time.localtime(1500000000))
'2017-07-14'

#字符串时间-->结构化时间    format_time --> struct_time (strptime)
#time.strptime(时间字符串,字符串对应格式)
>>>time.strptime("2017-03-16","%Y-%m-%d")
time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1)
>>>time.strptime("07/24/2017","%m/%d/%Y")
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)

#结构化时间 --> %a %b %d %H:%M:%S %Y串
#time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串
>>>time.asctime(time.localtime(1500000000))
'Fri Jul 14 10:40:00 2017'
>>>time.asctime()
'Mon Jul 24 15:18:33 2017'

#时间戳 --> %a %b %d %H:%M:%S %Y串
#time.ctime(时间戳)  如果不传参数,直接返回当前时间的格式化串
>>>time.ctime()
'Mon Jul 24 15:19:07 2017'
>>>time.ctime(1500000000)
'Fri Jul 14 10:40:00 2017' 

 

datetime模块

相比于time模块,datetime模块的接口更加直观,更容易调用

datetime模块定义了下面这几个类

  • datetime.date:表示日期的类。常用的属性有:year,month,day
  • datetime.time:表示时间的类。常用的属性有hour,minute,second,microsecong;
  • datetime.datetime:表示日期时间。
  • datetime.timedelta:表示时间间隔,即两个时间点之间的长度
  • date.tzinfo:与时区有关的相关信息。

我们需要记住的方法仅以下几个:

1.d=datetime.datetime.now()返回当前的datetime日期类型

d.timestamp(), d.today(), d.year, d.timetuple()    等方法可以调用

2.datetime.date.fromtimestamp(322222)把一个时间戳转为datetime日期类型

3.时间运算

>>> datetime.datetime.now()

datetime.datetime(2017, 10, 1, 12, 53, 11, 821218)

>>> datetime.datetime.now() + datetime.timedelta(4) #当前时间 +4天

datetime.datetime(2017, 10, 5, 12, 53, 35, 276589)

>>> datetime.datetime.now() + datetime.timedelta(hours=4) #当前时间+4小时

datetime.datetime(2017, 10, 1, 16, 53, 42, 876275)

4.时间替换

>>> d.replace(year=2999,month=11,day=30)

datetime.date(2999, 11, 30)

 

random模块

>>> import random
#随机小数(闭头闭尾)
>>> random.random()      # 大于0且小于1之间的小数 
0.7664338663654585
>>> random.uniform(1,3) #大于1小于3的小数
1.6270147180533838


#随机整数
>>> random.randint(1,5)  # 大于等于1且小于等于5之间的整数 (开头开尾)
>>> random.randrange(1,10,2) # 大于等于1且小于10之间的奇数  (开头闭尾)


#随机选择一个返回
>>> random.choice([1,'23',[4,5]])  # #1或者23或者[4,5]
#随机选择多个返回,返回的个数为函数的第二个参数
>>> random.sample([1,'23',[4,5]],2) # #列表元素任意2个组合
[[4, 5], '23']


#打乱列表顺序
>>> item=[1,3,5,7,9]
>>> random.shuffle(item) # 打乱次序
>>> item
[5, 1, 3, 7, 9]
>>> random.shuffle(item)
>>> item
[5, 9, 7, 1, 3]

生成随机验证码

import random

def v_code():

    code = ''
    for i in range(5):

        num=random.randint(0,9)
        alf=chr(random.randint(65,90))
        add=random.choice([num,alf])
        code="".join([code,str(add)])

    return code

print(v_code())
View Code

 

os模块

os模块是与操作系统交互的一个接口

os.makedirs('dirname1/dirname2')    可生成多层递归目录
os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()  删除一个文件
os.rename("oldname","newname")  重命名文件/目录
os.stat('path/filename')  获取文件/目录信息

os.system("bash command")  运行shell命令,直接显示
os.popen("bash command).read()  运行shell命令,获取执行结果
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd

os.path
os.path.abspath(path) 返回path规范化的绝对路径
os.path.split(path) 将path分割成目录和文件名二元组返回 
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 
os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path)  如果path是绝对路径,返回True
os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path)  如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path)  返回path所指向的文件或者目录的最后访问时间
os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小

注意:os.stat('path/filename')获取文件、目录信息的结构说明

stat 结构:

st_mode: inode 保护模式
st_ino: inode 节点号。
st_dev: inode 驻留的设备。
st_nlink: inode 的链接数。
st_uid: 所有者的用户ID。
st_gid: 所有者的组ID。
st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
st_atime: 上次访问的时间。
st_mtime: 最后一次修改的时间。
st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。
stat结构
os.sep    输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep    输出当前平台使用的行终止符,win下为"\r\n",Linux下为"\n"
os.pathsep    输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os模块的属性

sys模块

sys模块是与python解释器交互的一个接口

sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n)        退出程序,正常退出时exit(0)
sys.version        获取Python解释程序的版本信息
sys.maxint         最大的Int值
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称
sys.stdout.write('please:')  #标准输出 , 引出进度条的例子, 注,在py3上不行,可以用print代替
val = sys.stdin.readline()[:-1] #标准输入
sys.getrecursionlimit() #获取最大递归层数
sys.setrecursionlimit(1200) #设置最大递归层数
sys.getdefaultencoding()  #获取解释器默认编码
sys.getfilesystemencoding  #获取内存数据存到文件里的默认编码

序列化模块

什么叫序列化---将在内存的数据类型(字典、列表等)内容转换成一个字符串的过程就叫做序列化。

比如,我们在python代码中计算的一个数据需要给另外一段程序使用,那我们怎么给?
现在我们能想到的方法就是存在文件里,然后另一个python程序再从文件里读出来。
但是我们都知道,对于文件来说是没有字典这个概念的,所以我们只能将数据转换成字典放到文件中。
你一定会问,将字典转换成一个字符串很简单,就是str(dic)就可以办到了,为什么我们还要学习序列化模块呢?
没错序列化的过程就是从dic 变成str(dic)的过程。现在你可以通过str(dic),将一个名为dic的字典转换成一个字符串,
但是你要怎么把一个字符串转换成字典呢?
聪明的你肯定想到了eval(),如果我们将一个字符串类型的字典str_dic传给eval,就会得到一个返回的字典类型了。
eval()函数十分强大,但是eval是做什么的?e官方demo解释为:将字符串str当成有效的表达式来求值并返回计算结果。
BUT!强大的函数有代价。安全性是其最大的缺点。
想象一下,如果我们从文件中读出的不是一个数据结构,而是一句"删除文件"类似的破坏性语句,那么后果实在不堪设设想。
而使用eval就要担这个风险。
所以,我们并不推荐用eval方法来进行反序列化操作(将str转换成python中的数据结构)
为什么要有序列化模块

(网络传输,数据持久化)

序列化的目的

  1. 以某种存储形式使自定义对象持久化
  2. 将对象从一个地方传递到另一个地方
  3. 使程序根据维护性

用于序列化的两个模块

  • json,用于字符串和python数据类型间(非全部int\str\list\tuple\dict)进行转换
  • pickle,用于python特有的类型和python的数据类型间进行转换

Json模块提供了四个功能:dumps、dump、loads、load

pickle模块提供了四个功能:dumps、dump、loads、load

import pickle
data = {'k1':123,'k2':'Hello'}

# pickle.dumps 将数据通过特殊的形式转换位只有python语言认识的字符串
p_str = pickle.dumps(data)
print(p_str)

#pickle.dump 将数据通过特殊的形式转换位只有python语言认识的字符串,并写入文件
with open('D:/result.pk','wb',encoding='utf8') as fp:
    pickle.dump(data,fp)

import json
# json.dumps 将数据通过特殊的形式转换位所有程序语言都认识的字符串
j_str = json.dumps(data)
print(j_str)

#pickle.dump 将数据通过特殊的形式转换位只有python语言认识的字符串,并写入文件
with open('D:/result.json','wb',encoding='utf8') as fp:
    json.dump(data,fp)

json vs pickle:

JSON:

优点:跨语言、体积小

缺点:只能支持(int、str、list、dict、tuple)

Pickle:

优点:专门为python设计,支持python所有的数据类型

缺点:只能在python中使用,存储数据占空间大

shelve模块

shelve模块是一个简单的k,v 将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式。(pickle的高级封装,可多次dump和load)

序列化:

import shelve

f = shelve.open('shelve_test')    # 打开一个文件

names = ['alex', 'rain', 'test']
info = {'name':'alex','age':22}

f["names"] = names    # 持久化列表
f['info_dic'] = info

f.close()

反序列化

import shelve

d = shelve.open('shelve_test')    # 打开一个文件

d['names']
d['info_dic']

# del d['test']    # 还可以删除

 

shutil模块

高级的文件、文件夹、压缩包处理模块

shutil.copyfileobj(fsrc, fdst[, length])

将文件内容拷贝到另一个文件中

import shutil
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))

shutil.copyfile(src, dst)

拷贝文件

shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在

shutil.copymode(src, dst)

仅拷贝权限。内容、组、用户均不变

shutil.copymode('f1.log', 'f2.log') #目标文件必须存在

shutil.copy(src, dst)

拷贝文件和权限

import shutil
shutil.copy('f1.log', 'f2.log')

shutil.copy2(src, dst)

拷贝文件和状态信息

import shutil
shutil.copy2('f1.log', 'f2.log')

shutil.ignore_patterns(*patterns)

shutil.copytree(src, dst, symlinks=False, ignore=None)

递归的去拷贝文件夹

import shutil
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
# 目标目录不能不存在,注意对folder2目录要有可写权限,ignore的意思是排除

shutil.rmtree(path[, ignore_errors[, onerror]])

递归的去删除文件

import shutil
shutil.rmtree('folder1')

shutil.move(src, dst)

递归的去移动文件,它类似mv命令,其实就是重命名

import shutil
shutil.move('folder1', 'folder3')

shutil.make_archive(base_name, format,...)

创建压缩包并返回文件路径,例如:zip、tar

  • base_name:压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存当前目录,否则保存至指定路径。

如 data_bak => 保存至当前路径

如:/tmp/data_bak => 保存至/tmp/

  • format:压缩包种类,‘zip’,'tar','bztar','gztar'
  • owner:用户,默认是当前用户
  • group:组, 默认当前组
  • logger:用于记录日志,通常是logging.Logger对象
# 将 /data 下的文件打包放置当前程序目录
#将 /data 下的文件打包放置当前程序目录
import shutil
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')
# 压缩单个文件
ret = shutil.make_archive('xml_xml', "gztar")


#将 /data下的文件打包放置 /tmp/目录
import shutil
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')

shutil对压缩包的处理是调用ZipFile和TarFile两个模块来进行的,详细:

zipfile压缩&解压缩

import zipfile

# 压缩
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')
z.write('data.data')
z.close()

# 解压
z = zipfile.Zipfile('laxi.zip', 'r')
z.extractall(path='.')
z.close()

tarfile压缩&解压缩

import tarfile

# 压缩
t = tarfile.open(''/tmp/egon.tar'', 'w)
t.add(''/test1/a.py',arcname='a.bak'')
t.add('/test1/b.py',arcname='b.bak')
t.close()

#解压
t = tarfile.open('/tmp/egon.tar', 'r')
t.extractall('/egon')
t.close()

 

ConfigParser模块

此模块用于生产和修改常见配置文档,当前模块的名称在python3.x版本中变更为configparser。

常见的配置文件格式如下

[DEFAULT]
ServerAliveInterval = 45   
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

[topsecret.server.com]
Port = 50022
ForwardX11 = no

解析配置文件

import configparser    # 导入模块
config = configparser.ConfigParser()    # 实例化(生成对象)
config.sections()    # 调用sections方法
>>> []

config.read('example.ini')    # 读配置文件(注意文件路径)
config.sections()    # 调用sections方法(默认不会读取default)
>>>['bitbucket.org', 'topsecret.server.com']

'bitbucket.org' in config    # 判断元素是否在sections列表内
>>> True

'bytebong.com' in config
>>> False

config['bitbucket.org']['User']    # 通过字典的方式取值
>>> 'hg'

config['DEFAULT']['Compression']
>>> 'yes'

topsecret = config['topsecret.server.com']
topsecret['ForwardX11']
>>> 'no'

topsecret['Port']
>>> '50022'

for key in config['bitbucket.org']: print(key)
# for循环bitbucket.org  字典的key
>>> user
        compressionlevel
        serveraliveinterval
        compression
        forwardx11

config['bitbucket.org']['ForwardX11']
>>> 'yes'

其他增删改查语法

[group1] # 支持的两种分隔符“=”, “:”
k1 = v1
k2:v2

[group2]
k1 = v1

import ConfigParser

config = ConfigParser.ConfigParser()
config.read('i.cfg')

# ########## 读 ##########
secs = config.sections()
print(secs)
options = config.options('group2)    # 获取指定section的keys
print(options)

item_list = config.items('group2')    # 获取指定的section的keys & values, key value以元组的形式
print(item_list)

val = config.get('group1', 'key')    # 获取指定key的value
val = config.getini('group1', 'key')

# ########## 改写 ##########
sec = config.remove_section('group1')    # 删除section并返回状态(true, false)
config.write(open('i.cfg', "w"))    # 对应的删除操作要写入文件才会生效

sec = config.has_section('wupqiqi')
sec = config.add-section('wupeiqi')
config.write(open('i.cfg', 'w'))

config.set('group2', 'k1', 111)
config.write(open('i.cfg', 'w'))

config.remove_option('group2', 'age')
config.write(open('i.cfg', 'w'))

hashlib模块

加密算法介绍

HASH

Hash,一般翻译做“散列”,也有直接音译为”哈希”的,就是把任意长度的输入(又叫做预映射,pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。

简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数,

HASH主要用于信息安全领域中加密算法,他把一些不同长度的信息转化成杂乱的128位的编码里,叫做HASH值.也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系

MD5

什么是MD5算法

MD5信息摘要算法,一种被广泛使用的密码杂凑函数,可以产生出一个128位的散列值(hash value),用于确保信息传输完整一致。

MD5功能

输入任意长度的信息,经过处理,输出为128位的信息(数字指纹)

不同的输入得到不通的结果(唯一性);

MD5算法的特点

  1. 压缩性:任意长度的数据,算出的MD5值的长度都是固定的。
  2. 容易计算:从原数据计算出MD5值很容易。
  3. 抗修改性:对元数据进行任何改动,修改一个字节的MD5值区别也很大。
  4. 强抗碰撞:已知原数据和MD5,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

MD5算法是否可逆?

MD5不可逆的原因是:MD5是一种散列函数,使用的是hash算法,在计算过程中原文的部分信息是丢失了的。

MD5用途

  1. 防止被篡改
  2. 放置直接看到明文
  3. 放置抵赖(数字签名)

Python中提供相关的模块

import hashlib

m = hashlib.md5()
m.update(b"Hello")
m.update(b"It's me")
print(m.digest())
m.update(b"It's been a long time since last time we ...")

print(m.digest()) #2进制格式hash
print(len(m.hexdigest())) #16进制格式hash

import hashlib

# ######## md5 ########

hash = hashlib.md5()
hash.update('admin')
print(hash.hexdigest())

# ######## sha1 ########

hash = hashlib.sha1()
hash.update('admin')
print(hash.hexdigest())

# ######## sha256 ########

hash = hashlib.sha256()
hash.update('admin')
print(hash.hexdigest())


# ######## sha384 ########

hash = hashlib.sha384()
hash.update('admin')
print(hash.hexdigest())

# ######## sha512 ########

hash = hashlib.sha512()
hash.update('admin')
print(hash.hexdigest())

subprocess模块

除了os.system可以调用系统命令,,commands,popen2等也可以,比较乱,于是官方推出了subprocess,目地是提供统一的模块来实现对系统命令或脚本的调用

三种执行命令的方法

  • subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs)    # 官方推荐
  • subprocess.call(*popenargs, timeout=None, **kwargs) # 跟上面实现的内容差不多,另一种写法
  • subprocess.Popen()  # 上面各种方法的底层封装

run()方法

标准写法

subprocess.run(['df', '-h'], stderr=subprocess.PIPE, stdout=subprocess.PIPE, check=True)

涉及到管道 ' | ' 的命令需要这样写

subprocess.run('df -h | grep disk1', shell=True)
# shell = True的意思是这条命令直接交给系统去执行,不需要python负责解析

call()方法

# 执行命令,返回执行状态, 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])

# 执行命令,如果命令结果为0,就正常返回,否则抛异常
>>> subprocess.check_call(["ls", "-l"])
0

# 接收字符串格式命令,返回元组形式, 第1个元素是执行状态,第2个是命令结果
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')

# 接收字符串格式命令,并返回结果
>>> subprocess.getoutput('ls  /bin/ls')
'/bin/ls'

# 执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res
>>> res=subprocess.check_output(['ls', '-l'])
>>> res
b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'

logging模块

很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug(), info(), warning(), error() and critical()5个级别,下面我们看一下怎么用。

最简单用法

import logging

logging.warning("user [alex] attempted wrong password more than 3 times")
logging.critical("server is down")

输出

WARNING:root:user [alex] attempted wrong password more than 3 times
CRITICAL:root:server is down

如何把日志写到文件中

import  logging

logginh.basicConfig(filename='example.log', level=logging.INFO)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

其中下面这句中的level=logging.INFO意思是,把日志记录级别设置为INFO,也就是说,只有日记级别是INFO级别或者更高的日记才会被记录到文件里,在这个例子,第一条日志是不会被记录的,如果希望记录debug日志,那把日志级别改成DEBUG就行了。

logging.baseConfig(filename='example.log', level=logging.DEBUG)

自定义日志格式

感觉上面的日志格式忘记加上时间了,日志不知道时间怎么行呢,下面就来加上!

import logging
logging.basicConfig(format='%(asctime)s %(messagr)s', datefmt='%Y/%m/%d %I:%M:%S %p')

#输出
2010/12/12 11:46:36 AM is when this event was logged.

除了加时间,还可以自定义一大堆格式,下表就是所有支持的格式

%(name)sLogger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 用户输出的消息

日志同时输出到屏幕和文件

如果想同时把log打印在屏幕和日志文件里,就需要了解一点复杂的知识 了

Python使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:

  • logger提供了应用程序可以直接使用的接口;
  • handler将(logger创建的)日志发送到合适的目的输出;
  • filter提供了细度设备来决定输出哪条日志记录;
  • formatter决定日志记录的最终输出格式。

他们之间的关系是这样的:

 

每个组件的主要功能

logger

每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的Logger:

LOG = logging.getLogger("chat.gui")

而核心模块可以这样:

LOG = logging.getLogger("chat.kernel")

还可以绑定handler和filters

Logger.setlevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高

logger.addFilter(filt)、logger.removeFilter(fil):添加或删除指定的filter
logger.addHandler(hdlr)、logger.removeHandler(hdlr):增加或删除指定的handler

Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical(): 可以设置的日志级别

handler

handler对象负责发送相关的信息到指定目的地。Python的日志系统有很多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Handler可以把信息输出到文件,还有些Handler可以把信息发送到网咯上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler

Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略。
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象

每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:

1.logging.StreamHandler 使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。

2.logging.FileHandler 和StreamHandler 类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件

 

3.logging.handlers.RotatingFileHandler(根据文件大小)

这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的函数是:  

 

 RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])

其中filename和mode两个参数和FileHandler一样。

  • maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
  • backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的char.log.2并不会被更名,而是被删除。

4.logging.handlers.TimedRotatingFileHandler(根据时间)

这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的函数是:

TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])

其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。

interval是时间间隔。

when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:

  • S 秒
  • M 分
  • H 小时
  • D 天
  • W 每星期(interval==0时代表星期一)
  • midnight 每天凌晨

formatter组件

日志的formatter是个独立的组件,可以跟handler组合

fh = logging.FileHandler("access.log")
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

fh.setFormatter(formatter) #把formmater绑定到fh上

filter组件

如果你想对日志内容进行过滤,就可自定义一个filter

class IgnoreBackupLogFilter(logging.Filter):
    """忽略带db backup 的日志"""
    def filter(self, record): #固定写法
        return   "db backup" not in record.getMessage()

注意filter函数会返加True or False,logger根据此值决定是否输出此日志

然后把这个filter添加到logger中

logger.addFilter(IgnoreBackupLogFilter())

下面的日志就会把符合filter条件的过滤掉

logger.debug("test ....")
logger.info("test info ....")
logger.warning("start to run db backup job ....")
logger.error("test error ....")

一个同时输出到屏幕、文件、带filter的完成例子

import logging



class IgnoreBackupLogFilter(logging.Filter):
    """忽略带db backup 的日志"""
    def filter(self, record): #固定写法
        return   "db backup" not in record.getMessage()




#console handler
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
#file handler
fh = logging.FileHandler('mysql.log')
#fh.setLevel(logging.WARNING)


#formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
#bind formatter to ch
ch.setFormatter(formatter)
fh.setFormatter(formatter)

logger = logging.getLogger("Mysql")
logger.setLevel(logging.DEBUG) #logger 优先级高于其它输出途径的


#add handler   to logger instance
logger.addHandler(ch)
logger.addHandler(fh)



#add filter
logger.addFilter(IgnoreBackupLogFilter())

logger.debug("test ....")
logger.info("test info ....")
logger.warning("start to run db backup job ....")
logger.error("test error ....")

文件自动截断例子

import logging

from logging import handlers

logger = logging.getLogger(__name__)

log_file = "timelog.log"
#fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3)
fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3)


formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s')

fh.setFormatter(formatter)

logger.addHandler(fh)


logger.warning("test1")
logger.warning("test12")
logger.warning("test13")
logger.warning("test14")

 

posted @ 2019-03-26 17:24  下山打老虎i  阅读(715)  评论(0编辑  收藏  举报