Python-常用模块

random 模块

随机:在某个范围内取到每一个值的概率是相同的

importrandom

#随机小数

print(random.random())#0-1之内的随机小数

print(random.uniform(1, 5))#任意范围之内的随机小数

#随机整数

print(random.randint(1, 2))#[1,2] 包含2在内的范围内随机取整数

print(random.randrange(1, 2))#[1,2) 不包含2在内的范围内随机取整数

print(random.randrange(1, 10, 2))#[1,10) 不包含10在内的范围内随机取奇数

#随机抽取

lst = [1, 2, 3,'abc', ('wahaha','qqxing')]

ret= random.choice(lst)#随机抽取一个值

print(ret)

ret1= random.sample(lst, 2)#随机抽取两个值

print(ret1)

#打乱顺序 在原列表的基础上做乱序

lst = [1, 2, 3,'abc', ('wahaha','qqxing')]

random.shuffle(lst)

print(lst)

练习:生成随机验证码

#(1)4位数字的验证码

#基础版本

lis =''

foriinrange(4):

num= random.randint(0, 9)

lis+=str(num)

print(lis)

#函数版本

defrand_code(n=4):

lis=''

foriinrange(n):

num= random.randint(0, 9)

lis+=str(num)

returnlis

print(rand_code(6))

#(2)6位 数字+字母

defrand_code(n):

code=''

foriinrange(n):

rand_num= str(random.randint(0, 9))

rand_alph= chr(random.randint(97, 122))

rand_alph_upper= chr(random.randint(65, 90))

rand_num=random.choice([rand_num, rand_alph, rand_alph_upper])

code+=rand_num

returncode

ret= rand_code(4)

print(ret)

#(3)可控制验证码 数字 / 数字+字母

defrand_code(num, DefaultAlph=True):#当DefaultAlph=True时生成字母+数字的验证码, 为False时生成纯数字验证码

code =''

foriinrange(num):

rand_num= str(random.randint(0, 9))

ifDefaultAlph:

rand_alph= chr(random.randint(97, 122))

rand_alph_upper= chr(random.randint(65, 90))

rand_num=random.choice([rand_num, rand_alph, rand_alph_upper])

code+=rand_num

returncode

ret= rand_code(4, DefaultAlph=False)

print(ret)

time 模块

#常用方法

1.time.sleep(secs)

(线程)推迟指定的时间运行。单位为秒。

2.time.time()

获取当前时间戳

表示时间的三种方式

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

时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。

格式化的时间字符串(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 当前时区的名称

%% %号本身

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

认识Python中表示时间的几种格式:

#时间模块

importtime

#时间戳

print(time.time())#返回当前时间的时间戳

#结果>>> 1543743462.3950245

#时间字符串

print(time.strftime('%Y-%m-%d %X'))

#结果>>> 2018-12-02 17:39:58

print(time.strftime('%Y-%m-%d %H-%M-%S'))

#结果>>> 2018-12-02 17-39-58

#时间元组:localtime将一个时间戳转换为当前时区的struct_time

print(time.localtime())

#结果>>> time.struct_time(tm_year=2018, tm_mon=12, tm_mday=2, tm_hour=17, tm_min=43, tm_sec=44, tm_wday=6, tm_yday=336, tm_isdst=0)

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

time模块相关方法:

time.localtime([secs]):将一个时间戳转换为当前时区的struct_time;secs参数未提供,则以当前时间为准。

time.gmtime([secs]):和 localtime()类似;gmtime()方法是将一个时间戳转换为UTC时区(0 时区)的struct_time。

time.time():返回当前时间戳

time.mktime(t):将一个time.struct_time转为时间戳

time.sleep(secs):线程推迟指定的时间运行,单位为秒

time.asctime([t]):把一个表示时间的元组或者struct_time表示为这种形式:'Sun Dec 2 17:52:36 2018'。如果没有参数,默认将time.localtime()作为参数传入

time.ctime([t]):把一个时间戳(按秒计算的浮点数)转为time.asctime()的形式。如果参数未给或者为None的时候,默认将time.time()作为参数,相当于time.asctime(time.localtime(secs))

time.strftime(format[, t]):把一个代表时间的元组或者struct_time(如由time.localtime()和time.gmtime()返回)转为格式化的时间字符串,如果t未指定,默认传入time.localtime()

time.strptime(string[, format]):把一个格式化时间字符串转化为struct_time。实际上它和strftime()是逆操作

几种格式之间的转化

#时间戳——>结构化时间

#time.gmtime(时间戳) #UTC时间,与英国伦敦当地时间一致

#time.localtime(时间戳) #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间

print(time.gmtime(1510000000))

#结果>>> time.struct_time(tm_year=2017, tm_mon=11, tm_mday=6, tm_hour=20, tm_min=26, tm_sec=40, tm_wday=0, tm_yday=310, tm_isdst=0)

print(time.localtime())

#结果>>> time.struct_time(tm_year=2018, tm_mon=12, tm_mday=2, tm_hour=18, tm_min=4, tm_sec=23, tm_wday=6, tm_yday=336, tm_isdst=0)

#结构化时间——>时间戳

#time.mktime(结构化时间)

time_tuple = time.localtime(1510000000)

print(time.mktime(time_tuple))

#结果>>> 1510000000.0

#结构化时间——>字符串时间

#time.strftime("格式定义","结构化时间") 结构化时间参数若不传,则显示当前时间

print(time.strftime("%Y-%m-%d %X"))

#结果>>> 2018-12-02 18:07:47

print(time.strftime("%Y-%m-%d", time.localtime(1510000000)))

#结果>>> 2017-11-07

#字符串时间——>结构化时间

#time.strptime(时间字符串,字符串对应格式)

print(time.strptime("2018-02-22","%Y-%m-%d"))

#结果>>> time.struct_time(tm_year=2018, tm_mon=2, tm_mday=22, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=53, tm_isdst=-1)

print(time.strptime("2018/03/01","%Y/%m/%d"))

#结果>>> time.struct_time(tm_year=2018, tm_mon=3, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=60, tm_isdst=-1)

#结构化时间 ——> %a %b %d %H:%M:%S %Y串

#time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串

print(time.asctime(time.localtime(1510000000)))

#结果>>> Tue Nov 7 04:26:40 2017

print(time.asctime())

#结果>>> Sun Dec 2 18:12:19 2018

#时间戳 ——> %a %b %d %H:%M:%S %Y串

#time.ctime(时间戳) 如果不传参数,直接返回当前时间的格式化串

print(time.ctime())

#结果>>> Sun Dec 2 18:13:14 2018

print(time.ctime(1510000000))

#结果>>> Tue Nov 7 04:26:40 2017

计算本月一号的时间戳时间

datetime 模块

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

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

datetime.date:表示日期的类;常用的属性有year, month, day;

datetime.time:表示时间的类;常用的属性有hour, minute, second, microsecond;

datetime.datetime:表示日期时间。

datetime.timedelta:表示时间间隔,即两个时间点之间的长度。

datetime.tzinfo:与时区有关的相关信息。

importdatetime

print(datetime.datetime.now())#现在的时间

#只能调整的字段:weeks days hours minutes seconds

print(datetime.datetime.now() + datetime.timedelta(weeks=3))#三周后

print(datetime.datetime.now() + datetime.timedelta(weeks=-3))#三周前

print(datetime.datetime.now() + datetime.timedelta(days=-3))#三天前

print(datetime.datetime.now() + datetime.timedelta(days=3))#三天后

print(datetime.datetime.now() + datetime.timedelta(hours=5))#5小时后

print(datetime.datetime.now() + datetime.timedelta(hours=-5))#5小时前

print(datetime.datetime.now() + datetime.timedelta(minutes=-15))#15分钟前

print(datetime.datetime.now() + datetime.timedelta(minutes=15))#15分钟后

print(datetime.datetime.now() + datetime.timedelta(seconds=-70))#70秒前

print(datetime.datetime.now() + datetime.timedelta(seconds=70))#70秒后

current_time=datetime.datetime.now()

#可直接调整到指定的 年 月 日 时 分 秒 等

print(current_time.replace(year=1977))#直接调整到1977年

print(current_time.replace(month=1))#直接调整到1月份

print(current_time.replace(year=1989,month=4,day=25))#1989-04-25 18:49:05.898601

#将时间戳转化成时间

print(datetime.date.fromtimestamp(1232132131))#2009-01-17

sys 模块

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

sys.argv 命令行参数List,第一个元素是程序本身路径,(类似shell中调用脚本后面传入的$1,$2,$3)

sys.exit(n) 退出程序,正常退出时exit(0),错误退出sys.exit(1)

sys.version 获取Python解释程序的版本信息

sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值

sys.platform 返回操作系统平台名称

sys.argv用法

os 模块

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

#当前执行这个python文件的工作目录相关的工作路径

os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径

os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd

os.curdir 返回当前目录: ('.')

os.pardir 获取当前目录的父目录字符串名:('..')

#和文件夹相关

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.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"

os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"

os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为:

os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix'

# 和执行系统命令相关

os.system("bash command") 运行shell命令,直接显示

os.popen("bash command).read() 运行shell命令,获取执行结果

os.environ 获取系统环境变量

#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)是创建时间(详细信息参见平台的文档)。

练习

importos, sys

SIZE=

defcountDirSize(path):

globalSIZE

pathDirList=os.listdir(path)

forfileNameinpathDirList:

newAbsPath=os.path.join(path, fileName)

ifos.path.isdir(newAbsPath):

SIZE+=os.path.getsize(newAbsPath)

countDirSize(newAbsPath)

else:

SIZE+=os.path.getsize(newAbsPath)

defwin():

path= input('请输入需要统计的目录>>>')

ifos.path.exists(path):

countDirSize(path)

else:

print("请输入正确的路径...")

exit()

returnSIZE

deflinux():

path= sys.argv[1]

ifos.path.exists(path):

countDirSize(path)

else:

print("请输入正确的路径...")

exit()

returnSIZE

if__name__=='__main__':

ifos.name =="nt":

print(win())

elifos.name =="posix":

print(linux())

序列化模块

前提:

试想一下,有没有这种需求,想把一个字典或者列表直接存储起来,等下次再用,但是这些数据类型是存储在内存中的,程序结束后内存就被自动释放了。想把数据永久的存储下来,可以存储在内存里面,可是文件中只能存储字符串类型,那么这种怎么实现呢?比如打游戏过程中,打累了,停下来、关掉游戏,想过两天再玩,两天之后,游戏又从你上次停止的地方继续运行,你上次的游戏进度肯定是存储在硬盘上了,是以何种方式实现的呢?

可能你会想到强大的eval()内置函数,通过eval就能操作字符串。

stu = {'name':'xiaobai','age': 20}

ret=str(stu)

print(eval(ret), type(eval(ret)))

>>>

{'name':'xiaobai','age': 20}

但是强大的eval()的安全性却得不到保证,如果在文件中读取出来的是一条类似‘rm /’之类的命令,后果就不堪设想

所以在三个场景中是不建议使用eval函数的:

1、用户输入的时候  2、文件读入的时候  3、网络传输的时候

所以这个时候用到:序列化

序列化:

将一个对象从内存中转换为可存储(字符串类型)或者可传输(bytes)类型的过程,就叫做序列化。在python中叫做pickling,通俗讲:序列化就是将其他数据类型转换为字符串/bytes类型的过程。

为什么要使用序列化:

(1)持久化数据类型

(2)跨平台进行交互。不同的编程语言都用协商好的序列化格式,那么便能打破平台/语言之间的限制,实现跨平台数据交互。

(3)使程序更具维护性。

json

json格式在各个语言之间都是通用的序列化格式。在json中,所有的字符串都必须是" "双引号。

json的优点:

所有的数据类型都是各个语言通用的。在各个编程语言中都支持。

json的缺点:

1、json只是支持非常少的数据类型

2、对数据类型的约束十分严格

(1)字典中的key必须是字符串。

(2)json只支持列表,字典,数值,字符串,布尔值。

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

#dumps 与 loads

importjson

#序列化

dic = {'name':'xiaobai','age': 20,'sex':'nan', 2: 4}

str_dic= json.dumps(dic)#序列化:将一个字典转换为字符串

print(str_dic, type(str_dic))

#结果>>> {"name": "xiaobai", "age": 20, "sex": "nan", "2": 4}

#注意:json转换完的字符串类型的字典中的字符串是有""表示; 如果数字为key那么dump之后会强行转换为字符串数据类型

#反序列化

dic2 = json.loads(str_dic)#将一个字符串格式的字典转换成一个字典

print(dic2, type(dic2))

#结果>>> {'name': 'xiaobai', 'age': 20, 'sex': 'nan', '2': 4}

#注意:要用json的loads功能处理的字符串类型的字典中的字符串必须有""表示

#json是否支持元组,对元组做value的字典会把元组强制转换为列表

dic = {'a': (1, 2, 3)}

str_dic=json.dumps(dic)

print(str_dic)#{"a": [1, 2, 3]}

new_dic =json.loads(str_dic)

print(new_dic)#{'a': [1, 2, 3]}

#json是否支持元组做key?不支持,会报错

#dic = {(1, 2, 3): "a"}

#str_dic = json.dumps(dic) # TypeError: keys must be a string

#处理嵌套的数据类型

list_dic = [1, ['a','b','c'], 2, {'k1':'k2','k3':'k4'}]

str_dic=json.dumps(list_dic)

print(str_dic, type(str_dic))

#结果:[1, ["a", "b", "c"], 2, {"k1": "k2", "k3": "k4"}]

new_dic =json.loads(str_dic)

print(new_dic, type(new_dic))

#结果:[1, ['a', 'b', 'c'], 2, {'k1': 'k2', 'k3': 'k4'}]

如果想把数据类型直接序列化到一个文件中,那么就要使用到dump和load方法

#dump 与 load

#系列化进文件

importjson

dic= {'name':'xiaobai','age': 20,'sex':'nan', 2: 4}

with open('dump_json','w') as f:

json.dump(dic, f)#dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件

with open('dump_json') as f:

ret= json.load(f)#load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回

print(type(ret), ret)

# {'name': 'xiaobai', 'age': 20, 'sex': 'nan', '2': 4}

#能不能dump多个数据进入文件, dump可以多个数据进去,但是load不出来了,会报错

dic = {'name':'xiaobai','age': 20,'sex':'nan', 2: 4}

dic2= {'k1':'v1','k2':'v2'}

with open('dump_json','w') as f:

json.dump(dic, f)

json.dump(dic2, f)

#with open('dump_json') as f:

#json.load(f) # json.decoder.JSONDecodeError

#如果非要使用json dump多个数据到文件里面,那么就要用到dumps

dic = {'name':'xiaobai','age': 20,'sex':'nan', 2: 4}

dic2= {'k1':'v1','k2':'v2'}

with open('dump_json','w') as f:

str_dic1=json.dumps(dic)

str_dic2=json.dumps(dic2)

f.write(str_dic1+'\n')

f.write(str_dic2+'\n')

with open('dump_json') as f:

forlineinf:

ret=json.loads(line)

print(ret)

写入中文乱码,需要使用ensure_ascii关键字参数

#中文格式的

importjson

dic= {'中国':'北京','美国':'华盛顿'}

new_dic=json.dumps(dic)

print(new_dic)#{"\u4e2d\u56fd": "\u5317\u4eac", "\u7f8e\u56fd": "\u534e\u76db\u987f"}

new2_dic = json.dumps(dic, ensure_ascii=False)

print(new2_dic)#{"中国": "北京", "美国": "华盛顿"}

with open('dump_json','w') as f:

json.dump(dic, f)#写入文件的内容:{"\u4e2d\u56fd": "\u5317\u4eac", "\u7f8e\u56fd": "\u534e\u76db\u987f"}

with open('dump_json','w', encoding='utf-8') as f:

json.dump(dic, f, ensure_ascii=False)#写入文件的内容:{"中国": "北京", "美国": "华盛顿"}

importjson

data= {'username':['李华','二愣子'],'sex':'male','age':16}

json_dic2= json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)

print(json_dic2)

#结果:

'''

{

"age":16,

"sex":"male",

"username":[

"李华",

"二愣子"

]

}

'''

pickle

由于json格式对python数据类型的支持不是那么完美,如果只是在python程序之间交互,使用pickle模块的支持性会更好。但是不足之处就是,pickle只是适用于python语言。

pickle的优点:

(1)pickle支持python中的几乎所有数据类型

(2)pickle会把数据类型序列化为bytes类型

pickle的缺点:

(1)pickle只适用于python

pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load (不仅可以序列化字典,列表...可以把python中任意的数据类型序列化

importpickle

#dumps 与 loads

dic = {'name':'xiaobai','age': 20,'sex':'nan', 2: 4}

b_dic=pickle.dumps(dic)

print(type(b_dic))#

d_dic =pickle.loads(b_dic)

print(type(d_dic))#

#dump 与 load

dic = {'name':'xiaobai','age': 20,'sex':'nan', 2: 4}

with open('pickle_dump','wb') as f:

pickle.dump(dic, f)

with open('pickle_dump','rb') as f:

ret=pickle.load(f)

print(ret)#{'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4}

#可以发现pickle和json用法其实是完全一样,只是dump写和读的时候注意,因为pickle转换为bytes类型,所以写读时候都要以wb 和rb的形式

hashlib模块

算法介绍:

Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。

摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

hashlib介绍:

hashlib模块是一个内部有摘要算法的模块,而且内部可以给我们提供不止一种摘要算法。能够把 一个 字符串 数据类型的变量,转换成一个 定长的 密文的 字符串,字符串里的每一个字符都是一个十六进制数字

为什么需要hashlib?

对于同一个字符串,用相同的算法,相同的手段去进行摘要,获得的值总是相同的

hashlib模块的使用:

hashlib模块提供了多种摘要算法:

md5算法:定长32位 16进制(应用最广发的摘要算法)

sha系统算法:定长40位

sha算法要比MD5算法更加复杂,且sha N数值越大,算法越复杂,耗时越久,结果越长,但也更安全

importhashlib

#hashlib模块md5摘要算法的使用

name ="xiaobai"

password='xiaobai123'

#1.首先,需要先实例化一个md5的对象,一个对象只加密一个数据

md5_obj =hashlib.md5()

#2. update()方法,把需要进行md5的对象放入

md5_obj.update(password.encode('utf-8'))

#3. 通过hexdigest(),得到摘要算法之后的密文

md5_password =md5_obj.hexdigest()

print(md5_password)#21b3a6792936ba9c2ecbcbe0da8ba961

print(len(md5_password))#md5算法,定长32位

#hashlib模块sha摘要算法的使用

#操作和md5如出一辙,先创建对象,通过update加密,再通过hexdigest取值

name ="xiaobai"

password='xiaobai123'

sha_obj=hashlib.sha1()

sha_obj.update(password.encode('utf-8'))

sha_password=sha_obj.hexdigest()

print(sha_password)#6e96c5250d4d9c3b1ea9b5815d41aa0343a3c691

print(len(sha_password))#sha1算法,定长40位

hashlib的应用

用户登录的验证

name |password

-------+--------

xiaobai|xiaobai123

有一个用户小白,密码为xiaobai123,如果密码就这样明文存储,如果数据库被黑,那么密码就毫无保留的暴露给了黑客。所以这时候就需要用到摘要,在数据库中,存储密码的摘要信息,每次登陆的时候,再做摘要信息的对比

name |password

-------+--------

xiaobai|21b3a6792936ba9c2ecbcbe0da8ba961

所以每次登陆的时候,便需要进行密码信息的摘要对比

importhashlib

defget_md5_pwd(s):

md5_obj=hashlib.md5()

md5_obj.update(s.encode('utf-8'))

ret=md5_obj.hexdigest()

returnret

username= input("username>>>:").strip()

password= input("password>>>:").strip()

with open('userinfo', encoding='utf-8') as f:

forlineinf:

user, pwd= line.strip().split('|')

ifusername == userandget_md5_pwd(password) ==pwd:

print("登录成功")

break

else:

print("登录失败")

通过摘要算法的手段,虽然密码是用密文的形式存储了,但是在现在的攻击手段中,有一种叫做"撞库"的手段,就是通过一个存储着大量密码与md5后的摘要对应的关系,再一一进行匹配,如果摘要信息一致,便能够反推出密码,因为同一种算法的同一个字符串,结果总是不变的。那么,有什么方法能够防止撞库?那就通过加盐值得手段(1.固定盐值 2.更好的方法:动态加盐)

何为盐值(salt),其实就是给原数据+一段指定的字符串,这样得到的MD5值就会发生变化。只要颜值不被黑客知道,那么就很难反向推出原数据。

#加盐的md5算法,采用固定盐值(盐值:static)

username ="xiaobai"

password="xiaobai123"

md5_obj=hashlib.md5()

md5_obj.update('static'.encode('utf-8'))#加油

md5_obj.update(password.encode('utf-8'))

ret=md5_obj.hexdigest()

print(ret)

#动态加盐,通过把用户的唯一标识作为盐值,例如每个用户的用户名都是唯一

username ="xiaobai"

password="xiaobai123"

md5_obj=hashlib.md5()

md5_obj.update(username.encode('utf-8'))#动态加盐

md5_obj.update(password.encode('utf-8'))

ret=md5_obj.hexdigest()

print(ret)

文件一致性的校验

给一个文件中的所有内容进行摘要算法,得到一个md5结果。此时,我们可以体验到md5摘要算法的神奇的地方,对于同一个字符串,不管把他拆开多少段,最终得到的md5值都是一样。

#同一个字符串,不管拆开多少段,最终的md5都是一样的。

s ='hello world'

md5_obj=hashlib.md5()

md5_obj.update(s.encode('utf-8'))

ret=md5_obj.hexdigest()

print(ret)#5eb63bbbe01eeed093cb22bb8f5acdc3

md5_obj=hashlib.md5()

md5_obj.update('hello'.encode('utf-8'))

md5_obj.update('world'.encode('utf-8'))

ret=md5_obj.hexdigest()

print(ret)#5eb63bbbe01eeed093cb22bb8f5acdc3

所以对文件进行一致性校验

defget_file_md5(file_path):

file_md5_obj=hashlib.md5()

with open(file_path, encoding='utf-8') as f:

forlineinf:

file_md5_obj.update(line.encode('utf-8'))

ret=file_md5_obj.hexdigest()

returnret

综合版两个文件MD5对比

对视频文件进行一致性校验

一般是视频格式的文件/网络传输的文件,都是二进制的bytes类型。此时没有行的概念,该怎么做?此时,可以设置一个buffer,每次都读取相同长度的buffer.

logging模块

logging模块是用来操作日志的。

logging模块分为两种配置方式:(1)函数式简单配置。(2)logger对象配置

函数式简单配置

importlogging

logging.debug('debug message')

logging.info('info message')

logging.warning('warning message')

logging.error('error message')

logging.critical('critical')

默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。此时如果想改变显示,就需要在logging.basicConfig()中,把level级别调低

importlogging

logging.basicConfig(level=logging.DEBUG)

logging.debug('debug message')

logging.info('info message')

logging.warning('warning message')

logging.error('error message')

logging.critical('critical')

此时,如果不想日志默认输出到标准输出,想重定向输出到指定的日志文件中,那么也可以通过修改logging.basicconfig()中filename属性,指定重定向的文件。

importlogging

#通过修改logging.basicConfig(filename=‘file_path’),进行输出重定向

logging.basicConfig(level=logging.DEBUG, filename='loging.log')

logging.debug('debug message')

logging.info('info message')

logging.warning('warning message')

logging.error('error message')

logging.critical('critical')

basicConfig常用的参数及输出格式

总结:

通过logging模块的简单配置项可以完成对日志的基本操作,但是依然有几点痛点:

(1)basicConifg不支持输出中文

(2)日志的输出只能够文件/屏幕二选一,不能做到同时。

logger 对象配置

既然logging模块自带的basicConfig配置不能够解决中文问题,那么只能通过自己创建对象的方式来更加灵活的操作,解决固有的痛点:1.不能支持中文 2.同时向文件和屏幕输出内容

事前应该有几个准备事项:

(1)先实例化一个日志对象

(2)创建一个控制文件输出的文件操作符

(3)创建一个控制屏幕输出的屏幕操作符

(4)指定日志输出的格式(可以指定多个,文件输出和屏幕输出格式可以不同)

(5)文件操作符绑定一个日志格式

(6)屏幕操作符绑定一个日志格式

(7)日志对象绑定文件操作符以及屏幕操作符

importlogging

#(1) 创建一个log对象

logger =logging.getLogger()

#(2) 创建一个控制文件输出的文件操作符,encoding='utf-8‘’,解决中文问题

file_handler = logging.FileHandler('test.log', encoding='utf-8')

#(3) 创建一个控制屏幕输出的屏幕操作符

screen_handler =logging.StreamHandler()

#(4) 设置日志输出的格式

log_fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

#(5) 给文件操作符绑定一个日志格式

file_handler.setFormatter(log_fmt)

#(6) 给屏幕操作符绑定一个日志格式

screen_handler.setFormatter(log_fmt)

#(7) 日志对象绑定文件操作符和屏幕操作符

logger.addHandler(file_handler)

logger.addHandler(screen_handler)

#(8) 设置日志输出的级别

logger.setLevel(logging.DEBUG)

#(9) 打印日志

logger.debug('这是debug的消息')

logger.info('这是info的消息')

logger.warning('这是warning的消息')

logger.error('这是error的消息')

importlogging

#(1) 创建一个log对象

logger =logging.getLogger()

#(2) 创建一个控制文件输出的文件操作符,encoding='utf-8‘’,解决中文问题

file_handler = logging.FileHandler('test.log', encoding='utf-8')

#(3) 创建一个控制屏幕输出的屏幕操作符

screen_handler =logging.StreamHandler()

#(4) 设置日志输出的格式

log_fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

#(5) 给文件操作符绑定一个日志格式

file_handler.setFormatter(log_fmt)

#(6) 给屏幕操作符绑定一个日志格式

screen_handler.setFormatter(log_fmt)

#(7) 日志对象绑定文件操作符和屏幕操作符

logger.addHandler(file_handler)

logger.addHandler(screen_handler)

#(8) 设置日志输出的级别

logger.setLevel(logging.DEBUG)

#(9) 打印日志

deflogin():

username= input("username>>>:")

password= input("password>>>:")

ifusername =="xiaobai"andpassword =="xiaobai123":

logger.info("%s 登录成功"%username)

print("登录成功")

else:

logger.error("%s 登录失败,密码:%s"%(username, password))

print("登录失败")

login()

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200111A0MTSU00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券