week4 day3/4 常用模块

书接上回,上次我们讲到了日志模块。对于很多项目,记录用户/应用程序的日常操作和用户信息的变动都是非常必要的。因此日志模块在python日常使用中频率还是比较高的。今天,我们继续来介绍几个常见的模块,来提高我们日常编程效率和编程宽度。

一、正则模块 import re(字符串匹配)

正则就是用一些含有特殊含义大的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说:正则就是用来描述一类事物的规则。(在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。

1.1 基本操作和工作原理

在这里插入图片描述

import re
res=re.findall('abc','abcxxxabcyyyabc')
print(res)----->['abc','abc','abc']

通过这个小例子我们先来了解一些正则模块使用的语法规范。通过调用re模块下面的findall功能来进行下一步操作。而使用findall的语法标准是re.findall(搜索的字符串,被搜索的字符串)。而具体的工作机制是这样的,拿着搜索的字符串'abc'去和被搜索的字符串'abcxxxabcyyyabc'从头开始一个一个作比较,匹配到一样的字符继续匹配,直到完全匹配所有搜索的字符'abc'。如果匹配到了'ab'但是没有匹配到'c',则从'a'匹配到的那个字符的下一个字符重新开始匹配。

# 贪婪匹配 ? * + {}
import re
# ?左边那个字符出现0次或者1次
res=re.findall('da?','daa jhueadda')
print(res)-----># ['da', 'd', 'da']
print(re.findall('a\w?',' a abbbbbbbbbbbb ab'))-----># ['a','ab','ab']
print(re.findall('ab{0,1}',' a abbbbbbbbbbbb ab'))-----># ['a', 'ab', 'ab']

# *左边那个字符出现0次或无数次
print(re.findall('ab*',' a abbbbbbbbbbbb ab'))-----># ['a', 'abbbbbbbbbbbb', 'ab']
print(re.findall('ab{0,}',' a abbbbbbbbbbbb ab'))-----># ['a', 'abbbbbbbbbbbb', 'ab']

# +左边那个字符出现1次或无数次
print(re.findall('ab+',' a abbbbbbbbbbbb ab'))-----># ['abbbbbbbbbbbb', 'ab']
print(re.findall('ab{1,}',' a abbbbbbbbbbbb ab'))-----># ['abbbbbbbbbbbb', 'ab']
print(re.findall('a.*?b','a123b ab'))-----># ['a123b', 'ab']
print(re.findall('a.+?b','a123b ab'))-----># ['a123b']
# {n,m}:左边那一个字符出现n次或m次
print(re.findall('ab{2,4}',' a abbbbbbbbbbbb ab'))-----># ['abbbb']
print(re.findall('ab{2,4}',' a abb abbb abbbbbbbbbbbb ab'))-----># ['abb', 'abbb', 'abbbb']

# 出现在贪婪匹配后面的 ? 可以消除贪婪匹配的效果,见例
print(re.findall('a.*c','ayirfyg=-csfhoiushc'))-----># ['ayirfyg=-csfhoiushc']
print(re.findall('a.*?c','ayirfyg=-csfhoiushc'))-----># ['ayirfyg=-c']
# ()小括号不改变匹配规则,改变产出结果
print(re.findall("href='(.+?)'","< a href='https://www.baidu.com.cn'>'我特么是百度啊'</ a>< a href='https://www.sina.com.cn'>'我特么是新浪啊'</ a>"))-----># ['https://www.baidu.com.cn', 'https://www.sina.com.cn']
# 而如果想保留小括号分组查询功能,但是去除只保留分组结果的效果,可以在小括号内加?:
print(re.findall("href='(?:.+?)'","< a href='https://www.baidu.com.cn'>'我特么是百度啊'</ a>< a href='https://www.sina.com.cn'>'我特么是新浪啊'</ a>"))-----># ["href='https://www.baidu.com.cn'", "href='https://www.sina.com.cn'"]

print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company'))
print(re.findall('company|companies','Too many companies have gone bankrupt, and the next one is my company'))-----># ['companies', 'company']
# 重复匹配:| . | * | ? | .* | .*? | + | {n,m} |
# .代表除了换行符以外的任意字符
import re
res=re.findall('.','a1b26\n\tj')
print(res)-----># ['a', '1', '2','6','\t','j']
print(re.findall('a.c','a1c a2c aAc a+c a\nc aaaac'))-----># ['a1c', 'a2c', 'aAc', 'a+c', 'aac']
print(re.findall('a.c','a1c a2c aAc a+c a\nc aaaaccccccc',re.DOTALL))-----># ['a1c', 'a2c', 'aAc', 'a+c', 'a\nc', 'aac'] 
# re.DOTALL相当于让.可以匹配换行符,也可以匹配多字符里面的符合要求的字符串
print(re.findall('a.b','a1b')) #['a1b']
print(re.findall('a.b','a1b a*b a b aaab')) #['a1b', 'a*b', 'a b', 'aab']
print(re.findall('a.b','a\nb')) #[]
print(re.findall('a.b','a\nb',re.S)) #['a\nb']
print(re.findall('a.b','a\nb',re.DOTALL)) #['a\nb']同上一条意思一样

#*
print(re.findall('ab*','bbbbbbb')) #[]
print(re.findall('ab*','a')) #['a']
print(re.findall('ab*','abbbb')) #['abbbb']

#?
print(re.findall('ab?','a')) #['a']
print(re.findall('ab?','abbb')) #['ab']
#匹配所有包含小数在内的数字
print(re.findall('\d+\.?\d*',"asdfasdf123as1.13dfa12adsf1asdf3")) #['123', '1.13', '12', '1', '3']

#.*默认为贪婪匹配
print(re.findall('a.*b','a1b22222222b')) #['a1b22222222b']

#.*?为非贪婪匹配:推荐使用
print(re.findall('a.*?b','a1b22222222b')) #['a1b']

#+
print(re.findall('ab+','a')) #[]
print(re.findall('ab+','abbb')) #['abbb']

#{n,m}
print(re.findall('ab{2}','abbb')) #['abb']
print(re.findall('ab{2,4}','abbb')) #['abb']
print(re.findall('ab{1,}','abbb')) #'ab{1,}' ===> 'ab+'
print(re.findall('ab{0,}','abbb')) #'ab{0,}' ===> 'ab*'

#[]
print(re.findall('a[1*-]b','a1b a*b a-b')) #[]内的都为普通字符了,且如果-没有被转意的话,应该放到[]的开头或结尾
print(re.findall('a[^1*-]b','a1b a*b a-b a=b')) #[]内的^代表的意思是取反,所以结果为['a=b']
print(re.findall('a[0-9]b','a1b a*b a-b a=b')) #[]内的^代表的意思是取反,所以结果为['a=b']
print(re.findall('a[a-z]b','a1b a*b a-b a=b aeb')) #[]内的^代表的意思是取反,所以结果为['a=b']
print(re.findall('a[a-zA-Z]b','a1b a*b a-b a=b aeb aEb')) #[]内的^代表的意思是取反,所以结果为['a=b']

#\# print(re.findall('a\\c','a\c')) #对于正则来说a\\c确实可以匹配到a\c,但是在python解释器读取a\\c时,会发生转义,然后交给re去执行,所以抛出异常
print(re.findall(r'a\\c','a\c')) #r代表告诉解释器使用rawstring,即原生字符串,把我们正则内的所有符号都当普通字符处理,不要转义
print(re.findall('a\\\\c','a\c')) #同上面的意思一样,和上面的结果一样都是['a\\c']

#():分组
print(re.findall('ab+','ababab123')) #['ab', 'ab', 'ab']
print(re.findall('(ab)+123','ababab123')) #['ab'],匹配到末尾的ab123中的ab
print(re.findall('(?:ab)+123','ababab123')) #findall的结果不是匹配的全部内容,而是组内的内容,?:可以让结果为匹配的全部内容
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))#['http://www.baidu.com']
print(re.findall('href="(?:.*?)"','<a href="http://www.baidu.com">点击</a>'))#['href="http://www.baidu.com"']

#|
print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company'))
print(re.findall('a[1+]c','a1caca2c aAc a+c a\nc aaaac'))-----># ['a1c', 'a+c']
print(re.findall('a[0-9]c','a1c a2c aAc a+c a\nc aaaac a10c',re.DOTALL))-----># ['a1c', 'a2c']

print(re.findall('a[-+%*]c','a1c a2c aAc a+c a\nc aaaac a10c',re.DOTALL)) # 只能匹配中间a和c之间是加减乘除符号的字符。但注意,-左右两边不能同时有东西

print(re.findall('a[^-+%*]c','a1c a2c aAc a+c a\nc aaaac a10c',re.DOTALL)) # ^ 在中括号中代表取反的意思,除了中括号内的字符,都能匹配

1.2 re模块提供的其他匹配功能

# ===========================re模块提供的方法介绍===========================
import re
#1
print(re.findall('e','alex make love') )   #['e', 'e', 'e'],返回所有满足匹配条件的结果,放在列表里
#2
print(re.search('e','alex make love').group()) #e,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。

#3
print(re.match('e','alex make love'))    #None,同search,不过在字符串开始处进行匹配,完全可以用search+^代替match

#4
print(re.split('[ab]','abcd'))     #['', '', 'cd'],先按'a'分割得到''和'bcd',再对''和'bcd'分别按'b'分割

#5
print('===>',re.sub('a','A','alex make love')) #===> Alex mAke love,不指定n,默认替换所有
print('===>',re.sub('a','A','alex make love',1)) #===> Alex make love
print('===>',re.sub('a','A','alex make love',2)) #===> Alex mAke love
print('===>',re.sub('^(\w+)(.*?\s)(\w+)(.*?\s)(\w+)(.*?)$',r'\5\2\3\4\1','alex make love')) #===> love make alex

print('===>',re.subn('a','A','alex make love')) #===> ('Alex mAke love', 2),结果带有总共替换的个数


#6
obj=re.compile('\d{2}')

print(obj.search('abc123eeee').group()) #12
print(obj.findall('abc123eeee')) #['12'],重用了obj

1.3 补充

import re
print(re.findall("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")) #['h1']
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").group()) #<h1>hello</h1>
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").groupdict()) #<h1>hello</h1>

print(re.search(r"<(\w+)>\w+</(\w+)>","<h1>hello</h1>").group())
print(re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>").group())
#补充二
import re#使用|,先匹配的先生效,|左边是匹配小数,而findall最终结果是查看分组,所有即使匹配成功小数也不会存入结果#而不是小数时,就去匹配(-?\d+),匹配到的自然就是,非小数的数,在此处即整数#print(re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")) #找出所有整数['1', '-2', '60', '', '5', '-4', '3']#找到所有数字:print(re.findall('\D?(\-?\d+\.?\d*)',"1-2*(60+(-40.35/5)-(-4*3))")) # ['1','2','60','-40.35','5','-4','3']
#计算器作业参考:http://www.cnblogs.com/wupeiqi/articles/4949995.html
expression='1-2*((60+2*(-3-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'

content=re.search('\(([\-\+\*\/]*\d+\.?\d*)+\)',expression).group() #(-3-40.0/5)
#_*_coding:utf-8_*_
#在线调试工具:tool.oschina.net/regex/#
import re

s='''
http://www.baidu.com
egon@oldboyedu.com
你好
010-3141
'''

#最常规匹配
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('Hello\s\d\d\d\s\d{3}\s\w{10}.*Demo',content)
# print(res)
# print(res.group())
# print(res.span())

#泛匹配
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^Hello.*Demo',content)
# print(res.group())


#匹配目标,获得指定数据

# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^Hello\s(\d+)\s(\d+)\s.*Demo',content)
# print(res.group()) #取所有匹配的内容
# print(res.group(1)) #取匹配的第一个括号内的内容
# print(res.group(2)) #去陪陪的第二个括号内的内容



#贪婪匹配:.*代表匹配尽可能多的字符
# import re
# content='Hello 123 456 World_This is a Regex Demo'
#
# res=re.match('^He.*(\d+).*Demo$',content)
# print(res.group(1)) #只打印6,因为.*会尽可能多的匹配,然后后面跟至少一个数字


#非贪婪匹配:?匹配尽可能少的字符
# import re
# content='Hello 123 456 World_This is a Regex Demo'
#
# res=re.match('^He.*?(\d+).*Demo$',content)
# print(res.group(1)) #只打印6,因为.*会尽可能多的匹配,然后后面跟至少一个数字


#匹配模式:.不能匹配换行符
content='''Hello 123456 World_This
is a Regex Demo
'''
# res=re.match('He.*?(\d+).*?Demo$',content)
# print(res) #输出None

# res=re.match('He.*?(\d+).*?Demo$',content,re.S) #re.S让.可以匹配换行符
# print(res)
# print(res.group(1))


#转义:\

# content='price is $5.00'
# res=re.match('price is $5.00',content)
# print(res)
#
# res=re.match('price is \$5\.00',content)
# print(res)


总结:尽量精简,详细的如下
    尽量使用泛匹配模式.*
    尽量使用非贪婪模式:.*?
    使用括号得到匹配目标:用group(n)去取得结果
    有换行符就用re.S:修改模式


#re.search:会扫描整个字符串,不会从头开始,找到第一个匹配的结果就会返回

# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# res=re.match('Hello.*?(\d+).*?Demo',content)
# print(res) #输出结果为None

#
# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# res=re.search('Hello.*?(\d+).*?Demo',content) #
# print(res.group(1)) #输出结果为



#re.search:只要一个结果,匹配演练,
import re
content='''
<tbody>
<tr id="4766303201494371851675" class="even "><td><div class="hd"><span class="num">1</span><div class="rk "><span class="u-icn u-icn-75"></span></div></div></td><td class="rank"><div class="f-cb"><div class="tt"><a href="/song?id=476630320"><img class="rpic" src="http://p1.music.126.net/Wl7T1LBRhZFg0O26nnR2iQ==/19217264230385030.jpg?param=50y50&quality=100"></a><span data-res-id="476630320" "
# res=re.search('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
# print(res.group(1))


#re.findall:找到符合条件的所有结果
# res=re.findall('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
# for i in res:
#     print(i)

#re.sub:字符串替换
import re
content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'

# content=re.sub('\d+','',content)
# print(content)


#用\1取得第一个括号的内容
#用法:将123与456换位置
# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# # content=re.sub('(Extra.*?)(\d+)(\s)(\d+)(.*?strings)',r'\1\4\3\2\5',content)
# content=re.sub('(\d+)(\s)(\d+)',r'\3\2\1',content)
# print(content)




# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# res=re.search('Extra.*?(\d+).*strings',content)
# print(res.group(1))


# import requests,re
# respone=requests.get('https://book.douban.com/').text

# print(respone)
# print('======'*1000)
# print('======'*1000)
# print('======'*1000)
# print('======'*1000)
# res=re.findall('<li.*?cover.*?href="(.*?)".*?title="(.*?)">.*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span.*?</li>',respone,re.S)
# # res=re.findall('<li.*?cover.*?href="(.*?)".*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span>.*?</li>',respone,re.S)
#
#
# for i in res:
#     print('%s    %s    %s   %s' %(i[0].strip(),i[1].strip(),i[2].strip(),i[3].strip()))

1.4 作业

import re

# 1、匹配密码,密码必须是由6位数字与字母组成,并且不能是纯数字也不能是纯字母
# 1.1 知识点:# ?!pattern,表示在没有配到pattern的字符串的前提下,再进行后续的正则表达式匹配,后续匹配仍然从被匹配字符串的头开始

# 1.2 答案:
print(re.search("(?!^[0-9]+$)(?!^[a-zA-Z]+$)^[0-9A-Za-z]{6}$","123asf").group())  # 123asf

# 1.3 解释:
# 上述正则的意思为:在匹配(?!^[0-9]+$)以及(?!^[a-zA-Z]+$)过后,如果字符串成功后在从头去匹配(?!^[a-zA-Z]+$),最终匹配完。


# 2、匹配密码,密码强度:强,必须包含大写,小写和数字,和特殊字符(!,@,#,%,&),且大于6位
# 2.1 知识点:# ?=pattern,表示在配到pattern的字符串的前提下,再进行后续的正则表达式匹配,后续匹配仍然从被匹配字符串的头开始

# 2.2 答案:
# while True:
#     pwd = input("please your password: ").strip()  # 比如输入:Aa3@adf123
#     pwd_pattern= re.compile("(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#%&])^([a-zA-Z0-9!@#%&]){6,}$")
#     if pwd_pattern.search(pwd) is None:
#         print("密码强度不够")
#     else:
#         break

# 2.3 解释:
# 上述正则表达式的意思:
#(1)首先是(?=.*[A-Z])匹配,.*表示密码中可以包含多个字符,[A-Z]代表密码中需要包含至少一个大写字母,注意一定不要去掉.*写成(?=[A-Z]),那样表示密码只能由一个字符组成,该字符是大写字母
#(2)其次是(?=.*[a-z])匹配,同上,确保密码中必须至少有一个小写字母
#(3)然后是(?=.*[0-9])匹配,同上,确保密码中必须至少有一个数字
#(4)然后是(?=.*[!@#%&])匹配,同上,,确保密码中必须至少有一个特殊符号!@#%&

#(5)最后是^([a-zA-Z0-9!@#%&]){6,}$,确保密码是由[a-zA-Z0-9!@#%&]字符构成,至少有6位


# 3、匹配email
# print(re.findall("(?:[a-zA-Z0-9]+)@(?:[0-9a-zA-Z]+).com","18611323113@163.com xxx@qq.com"))

# 4、匹配身份证
# your_id=input(">>: ").strip()
# print(re.findall("^([0-9]){17}([0-9]|X)$",your_id)) # 17个数字组成,最后一个字符可以是数字或X

# 5、匹配用户名,包含字母或者数字,且8位
# print(re.findall("^[0-9a-zA-Z]{8}$","egonlinh"))

# 5.1、要求输入的内容只能是汉字
# name=input('>>: ').strip()
# print(re.search(r'[\u4E00-\u9fa5]+',name))

# 6、取出字符串里的数字
# print(re.findall(r'\d+(?:\.\d+)?', 'sww123kw11.333e2lkd'))

# 7、取出所有负整数
# print(re.findall(r'-\d+', '-12,3,54,-13.11,64,-9'))  # 错误答案
# print(re.findall(r'(?!-\d+\.\d+)-\d+', '-12,3,54,-13.11,64,-9'))  # 正确答案

# 8、所有数字
# print(re.findall(r'\-?\d+(?:\.\d+)?', '-12.9,3.92,54.11,64,89,-9,-45.2'))

# 9、所有负数
# print(re.findall(r'\-\d+(?:\.\d+)?', '-12.9,3.92,54.11,64,89,-9,-45.2'))

# 10、所有的非负浮点数
print(re.findall(r'\d+\.\d+', '-12.9,3.92,54.11,64,89,-9,-45.2'))

# 11、
msg = """
中文名 贝拉克·侯赛因·奥巴马 
外文名 Barack Hussein Obama II 
别名 欧巴马 
性 别 男
国籍 美国 
民 族 德裔族 
出生地 美国夏威夷州檀香山 
出生日期 1961年8月4日 
职 业政治家、律师、总统 
毕业院校 哥伦比亚大学,哈佛大学 
信 仰新教 
主要成就 1996年伊利诺伊州参议员 
主要成就 美国第56届、57届总统 2009年诺贝尔和平奖获得者 时代周刊年度风云人物2008、2011 任期内清除本·拉登 
代表作品 《我相信变革》《我父亲的梦想》《无畏的希望》 
所属政党美国民主党 
血 型 AB型 
学 院西方学院 
妻 子 米歇尔·拉沃恩·奥巴马 

"""
#外文名
print(re.findall("外文名 (.*)",msg))

#出生日期
print(re.findall('出生日期 (\d{4})年(\d+)月(\d+)日',msg))

#妻子姓名
print(re.findall('妻 子 (\S+)',msg))

二、序列化模块 import json/pickle(存档和跨平台共享数据)

序列化就是把某种数据格式转变成字符串格式保存在文件里面,保存在硬盘里面。
反序列化就是把文件里面的字符串转化成原来的数据类型方便使用。
(我们之前学过的字符编码只针对了字符串数据类型,序列化针对几乎所有数据类型。)

序列化的作用:

  1. 存档和读档,保存数据状态
  2. 跨平台合并数据
# 补充知识点:eval可以运行字符串里面的代码
eval('print(111)')-----># 111

2.1 json模块

优点:跨平台性强(java,python,c都可以读)
缺点:并非所有数据类型都能覆盖(python的tuple无法存储)

import json
# json序列化 dump/dumps
dic={"name":123,"xxx":True,'yyy':None,'zzz':1.3}
dic_json=json.dumps
print(dic_json,type(dic_json))-----># {"name": 123, "xxx": true, "yyy": null, "zzz": 1.3} <class 'str'>
with open('a.json',mode='wt',encoding='utf-8') as f:
    f.write(dic_json)
# 上面的json序列化和写入文件两步操作可以合二为一,也就是dump操作
# dump语法:json.dump(被序列化内容,存储路径)
json.dump(dic,open('a.json',mode='wt',encoding='utf-8'))

# json反序列化 load/loads
res=json.loads(dict_json)
print(res,type(res))-----># {'name': 123, 'xxx': True, 'yyy': None, 'zzz': 1.3} <class 'dict'>
with open('a.json',mode='rt',encoding='utf-8') as f:
    res=f.read()
    res_json=json.loads(res)
# 上面的json反序列化和从文件读出两步操作可以合二为一,也就是load操作
# load语法:json.load(打开文件的方式)
resdic=json.load(open('a.json',mode='rt',encoding='utf-8'))
print(resdic)----->被序列化的内容

2.2 pickle模块

缺点:跨平台性差(python所有数据类型都覆盖,导致有些python独有数据类型无法被别的语言识别)
优点:数据类型广,语言独有性强

2.3 ujson模块(猴子补丁)

ujson模块在序列化和反序列化的效率都比json模块要稍高一些,因此可以在导入json模块前可以先打上猴子补丁,具体操作见代码:

import ujson
def monkey_patch_json():
    json.dumps=ujson.dumps
    json.dump=ujson.dump
    json.loads=ujson.loads
    json.load=ujson.load
monkey_patch_json()

三、随机模块 import random(验证码)

import random
print(random.random())#(0,1)----float    大于0且小于1之间的小数
print(random.randint(1,3))  #[1,3]    大于等于1且小于等于3之间的整数
print(random.randrange(1,3)) #[1,3)    大于等于1且小于3之间的整数
print(random.choice([1,'23',[4,5]]))#1或者23或者[4,5]
print(random.sample([1,'23',[4,5]],2))#列表元素任意2个组合
print(random.uniform(1,3))#大于1小于3的小数,如1.927109612082716

item=[1,3,5,7,9]
random.shuffle(item) # 打乱item的顺序,相当于‘洗牌’
# 小练习:做验证码
# 补充知识点:ord(),chr()
print(ord('a'))---->97
print(ord('z'))---->122
print(ord('A'))---->65
print(ord('Z'))---->90
# 验证码一般为6位,由数字,大写字母和小写字母组成的字符串
import random
def make_verify_code(max_size=6):
    str1=''
    for i in range(max_size):
        numpart=random.randint(0,9)
        upper_char=chr(random.randint(65,90))
        lower_char=chr(random.randint(97,122))
        str1+=random.choice([str(numpart),upper_char,lower_char])
    return str1
res=make_verify_code()
print(res)

四、哈希算法模块 import hashlib(检验文件传输完整性和密码加盐)

hash算法:传入一段内容会得到一段hash值。

hash值有三大特点:

  1. 如果传入的内容与采用的算法一样,无论hash次数,得到的hash值都一样。
  2. 只要采用的算法是固定的,无论传入的值有多大,hash值的长度就是固定的,不会随着内容的增多而变长。
  3. hash值不可逆,即不能通过hash值反解出内容是什么

----------------------------------------------特点1+特点3可以校验文件的完整性。----------------------------------------------
当我们从网上下载任务完成时,我们通常需要等待一段时间。这段时间就是在检查下载完毕文件的完整性。这就要求我们从服务端下载时,除了被下载的具体内容外,还需要下载对应的hash值。下载完毕后,需要在用户端用同样的hash算法对下载内容hash,与下载文件的hash值做比对,如果两个hash值相同,说明文件下载完整。
----------------------------------------------特点1+特点2可以给密码加密。----------------------------------------------
服务端在用户注册时,只需要明确hash算法,保存用户的用户名和密码的hash值即可。每次用户登陆时,会先在用户端把密码使用服务端规定的hash算法拿到hash值,再将用户名和密码的hash值打包发送给服务端。服务端拿到用户输入的账号和密码的hash值,与数据库信息作比较。如果匹配,则用户登陆成功。

# hashilib模块使用语法:
import hashlib
m=hashlib.md5()
m.upadate('你好'.encode('utf-8'))
print(m.hexdigit())-----># 拿到你好在md5算法下的hash值
# 必须要先指定hash算法,然后利用update传入需要被hash的数据。最后hexdigit吐出hash值
# 校验文件完整性的具体操作:
mm=hashlib.md5()
with open(文件路径,mode='rb') as f:
	for line in f:
		mm.update(line)
	print(mm.hexdigit())
# 但是这种方法存在一个问题,如果文件很大,则需要把所有内容都在用户端拿到hash值与从服务端下载的hash值作比较,很耗费时间
# 因此,我们可以通过f.seek()移动指针,再通过f.read(字符)把取出一定数量字符,分段比较,可以大大减少检验文件完整性的时间,来代码实现:
mm=hashlib.md5()
with open(文件路径,mode='rb') as f:
    res=f.read(10)
    mm.update(res)
    while f.seek(10,1) <= f.seek(0,2):
        res1=f.read(10)
        mm.update(res1)
    print(mm.hexdigest())
# 文件加密操作:
# 如果我们的密码只是简单设置很可能被不法分子暴力破解,因此,我们可以通过在密码加盐的操作让不法分子提高破解密码的时间
mm=hashlib.md5()
mm.update('宝塔镇河妖')
mm.update('123wth')
mm.update('天王盖地虎')
print(mm.hexdigit())
# 我们原本的密码前后被添加了其他字符,哪怕破解后也不好确定哪部分是真正的密码
但其实密码加盐也不是那么安全。黑客可以截获到的hash值不破译。而直接再写一个跳过密码hash算法的程序,直接把账号和hash完的密码发送给服务端。这种黑客方法的破解方案在后面会着重介绍。

五、子进程模块 import subprocess

我们在刚开始学习python的时候接触过进程的概念。进程是正在运行的程序。进程在内存中是相互隔离的,彼此不影响。

我们可以在Windows中使用cmd,输入tasklist来查询当前的所有进程。我们能不能在pycharm中通过同样的命令查询进程的数量呢?

import subprocess
import time
obj=subprocess.Popen('tasklist',
					shell=True)
time.sleep(1)

我们在运行这段代码时,产生了一个这段代码所在文件的进程。这段代码的功能是产生一个子进程。但是如果代码只写到这里,运行完这段代码后还未来得及把子进程的结果显示出来,文件的进程就结束了。所以需要导一下时间模块,让大进程原地等待子进程显示出结果。但是这种方法会让大进程停顿过长时间。我们需要用到subprocess里面又一个很骚的功能。在此之前,我们需要理清楚一个事情:两个进程是相互隔离的,所以子进程的结果无法直接被大进程获得。我们需要一个“管道”,让子进程把它产生的结果扔进去,大进程再从“管道”中把子进程的结果取出来。对代码作出修改:

import subprocess
obj=subprocess.Popen('tasklist',
					shell=True,
					stdout=subprocess.PIPE,
					stderr=subprocess.PIPE)
obj_stdout=obj.stdout.read() # 这个命令就在原地等,等子进程运行产生结果就拿到结果,如果没有结果,就在原地等。这个等待时间恰到好处
obj_stderr=obj.stderr.read() # 只有当命令产生错误结果的时候,子进程才会把错误新信息写到这个管道里面,才可以继续读到错误信息

print(obj_stdut) # 打印出来的结果是bytes类型。因为tasklist是windows的命令,windows默认字符编码格式是gbk
print(obj_stdout.decode('gbk')) # 输出结果正常
print(obj_stderr) # 如果产生错误信息会放在这里面

六、os/sys模块 import os/sys(重要!)

import os
# 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命令,直接显示

print(os.environ)  # 获取系统环境变量 # ---------------------登录用户后在其他文件其他位置都可以用的到

# os.path.abspath(path)  返回path规范化的绝对路径

print(os.path.abspath('a/b/c'))

# os.path.split(path)  将path分割成目录和文件名二元组返回
# os.path.dirname(path)  返回path的目录。其实就是os.path.split(path)的第一个元素

print(os.path.dirname(__file__)) # 目录名 E:/pycharm/notes and homework/w4 1.4/day2 04日志模块

print(os.path.basename(__file__)) # 文件名 day4 03 os和sys模块.py

# 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的大小

res=os.path.normpath(os.path.join(__file__,'..','..'))
print(res)

import sys
print(sys.argv) # 获取运行python程序时的参数
                # 拿到的是['E:/pycharm/notes and homework/w4 1.4/day4 03 os和sys模块.py']
# 修改复制文件的时候直接从cmd拿到运行文件名,原文件名,目标文件名
src_file=sys.argv[1]
dst_file=sys.argv[2]

七、配置模块 import configparser(重要!)

在之前我们学习文件目录管理规范时,我们把所有的变量都放在conf文件夹下面的settings.py里面了,把settings.py文件当模块导入其他文件,但可能会出现循环导入的情况。这里,nb的python第三方库又给我们提供了一种模块来解决可能存在的循环导入的问题。

我们需要先建立一个文件,起名为config.ini或者config.cnf。把我们之前放在settings.py文件里面的数据放在这个配置文件里面。下图是配置文件中的数据:
在这里插入图片描述
现在我们通过代码尝试把数据取出:

import configparser
config=configparser.ConfigParser()
config.read('config.ini') # 先指定要读的文件

res=config.sections() # 知道这个文件里面有几个sections
print(res)-----># ['section1', 'section2']
res1=config.options('sections') # 我们拿到了section1里面所有的’key‘
print(res1)-----># ['k1', 'k2', 'user', 'age', 'is_admin', 'salary']
res2=config.items('section1') # items可以取到对应sections的所有内容
print(res2)-----># [('k1', 'v1'), ('k2', 'v2'), ('user', 'egon'), ('age', '18'), ('is_admin', 'true'), ('salary', '31')]
res3=config.get('section1','k1') # 拿section1里面k1对应的值
print(res3,type(res3))-----># v1 <class 'str'>

八、时间模块 import time/datetime(重要!)

时间模块也是python提供的非常强大的模块,需要非常熟悉。

时间主要分成三种形式:

  1. 时间戳,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。
  2. 格式化的字符串对应time.strftime(format)
  3. 结构化的时间对应time.localtime()。struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)
import time
time.time()
time.sleep(秒数)
print(time.strftime('%Y-%m-%d %H:%M:%S'))-----># 2021-01-07 18:29:59
print(time.strftime(%T))-----># 如果你只想要时间的时分秒格式,%T即可。18:30:59
res=time.localtime()-----># time.struct_time(tm_year=2021, tm_mon=1, tm_mday=7, tm_hour=18, tm_min=31, tm_sec=58, tm_wday=3, tm_yday=7, tm_isdst=0)

上述三种时间格式是可以相互转换的。具体的转换方法参照图片:
在这里插入图片描述
代码演示:

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

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

# mktime(t) : 将一个struct_time转化为时间戳。
print(time.mktime(time.localtime()))#1473525749.0


# strftime(format[, t]) : 把一个代表时间的元组或者struct_time(如由time.localtime()和
# time.gmtime()返回)转化为格式化的时间字符串。如果t未指定,将传入time.localtime()。如果元组中任何一个
# 元素越界,ValueError的错误将会被抛出。
print(time.strftime("%Y-%m-%d %X", time.localtime()))#2016-09-11 00:49:56

# time.strptime(string[, format])
# 把一个格式化时间字符串转化为struct_time。实际上它和strftime()是逆操作。
print(time.strptime('2011-05-05 16:37:06', '%Y-%m-%d %X'))
#time.struct_time(tm_year=2011, tm_mon=5, tm_mday=5, tm_hour=16, tm_min=37, tm_sec=6,
#  tm_wday=3, tm_yday=125, tm_isdst=-1)
#在这个函数中,format默认为:"%a %b %d %H:%M:%S %Y"。

除了time以外,还有一个时间模块也非常好用,就是datetime模块。当我们计算时间差时,可以把所有时间统一成time.time()计算差值,我们也可以通过datetime.timedelta来比较。第二种方法更为方便,我们现在来介绍这种方法:

import datetime
print(datetime.datetime.now(),type(datetime.datetime.now())) # 2021-01-06 17:49:42.948038 <class 'datetime.datetime'>
res=datetime.datetime.now()+datetime.timedelta(days=3.5)
print(res) # 当前时间三天半以后的时间
print(datetime.datetime.now()>res) # 比较现在时间和拿到的时间的先后顺序
print(datetime.datetime.now()-datetime.timedelta(days=3.5)) # 当前时间三天半以前的时间

# 判断时间间隔,和时间先后!!!在把字符串的时间(记录在文件里面的时间)和现在时间对比时非常好用!!!
res=datetime.datetime.strptime('2016-09-11 00:49:56','%Y-%m-%d %H:%M:%S')
# print(res,type(res))
print(datetime.datetime.now()<res)

九、产生定长值 struct模块(网络编程解决粘包问题要用)

为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存取出定长的报头,然后再取真实数据。

该模块可以把一个类型,如数字,转成固定长度的bytes

>>> struct.pack('i',1111111111111)
struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值