常用模块

为什么要有模块?

    python有很多现成的方法,但是这些函数不能全部放在内存里,为用户节省很多不必要的内存消耗,用不到的功能就不用导入到内存里了。

什么是模块

    一块python代码

    一组python代码

    一组C语言代码

    一堆写好的 现成的可以用的类,函数等等

    以功能来分类的

模块类型

    1、内置模块:随着python解释器的安装可以直接使用的模块

    2、扩展模块:需要安装才能使用的模块,例如:django,itchat

    3、自定义模块:用户根据自己的需求完成的一组功能

时间模块time

import time  # 导入模块

时间戳 -- 计算机的时间

格式化时间 -- 人能看懂的时间

sleep('秒数')      让程序睡一段时间
time()          返回时间戳
strftime('%Y-%m-%d')    指定时间格式返回时间字符串
localtime()          返回时间元祖
strptime('2018-06-30','%Y-%m-%d')  用于格式化时间转换为结构化时间
asctime(结构化时间)      结构化时间转换为字符时间
ctime(时间戳)         时间戳转换为字符串时间

strftime()  # 字符串时间

print(time.strftime('%Y-%m-%d %H:%M:%S'))
2018-06-30 13:52:02

print(time.strftime('%Y')) #
print(time.strftime('%m')) #
print(time.strftime('%d')) #
print(time.strftime('%H')) #
print(time.strftime('%M')) #
print(time.strftime('%S')) #
print(time.strftime('%c')) # 字符串时间
View Code

localtime() 时间元祖

print(time.localtime())
time.struct_time(tm_year=2018,  # 当前是哪年
                 tm_mon=6,   # 当前月份
                 tm_mday=30,  # 当前日期
                 tm_hour=12,  # 当前时间
                 tm_min=2,   # 当前分钟
                 tm_sec=36,   # 当前秒数
                 tm_wday=5,  # 这个星期的第几天 0-6 七天
                 tm_yday=181, # 这一年的第几天
                 tm_isdst=0)  # 是否是夏令时
View Code

sleep 时间阻塞

time.sleep(2)
print('2')
View Code

时间戳time()

print(time.time())
1530337804.89982 # 从1970年1月1日到现在的秒数
View Code

转换关系

 

时间戳(timestamp) --> 结构化时间(struct_time)

t1 = time.time()  # 获取当前时间戳
t1 = 1987654321  # 自己定义一个时间戳(timestamp)
t2 = time.localtime(t1)  # 时间戳转换成结构化时间(struct_time)用localtime
print(t2)
View Code

结构化时间(struct_time) --> 时间戳(timestamp)

t3 = time.localtime()  # 生成结构化时间(struct_time)
t4 = time.mktime(t3)   #  结构化时间转换为时间戳(timestamp)用mktime
print(t4)
View Code

结构化时间(struct_time) --> 格式化时间(format_time)

t6 = time.strftime('%Y-%m-%d %H:%M:%S')  # 结构化时间转换到格式化时间只能传一个字符串。用strftime
print(t6)
View Code

格式化时间(format_time) --> 结构化时间(struct_time)

t7 = time.strptime('2018-09-10','%Y-%m-%d')  # 定义的字符串时间和格式必须一一对应
print(t7)
time.struct_time(tm_year=2018, tm_mon=9, tm_mday=10,

                 tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0,

                 tm_yday=253, tm_isdst=-1)
View Code

随机数模块randmon

import random

关于小数random(),uniform(n,m)

print(random.random())  # 随机生成大于0小于1的小数
print(random.uniform(1,5))  # 随机生成大于等于1小于5的小数

关于整数randint(n,m),randrange(n,m)

print(random.randint(1,6))  # 随机生成1-6之间的任意一个数
print(random.randrange(1,6)) # 随机生成大于等于1小于6的一个数

随机返回一个数choice(n1,n2,n3,...)

print(random.choice([1,10,[9,5,]]))  # 随机返回一个数

随机返回指定个数

print(random.sample(n1,n2,n3,...,number))  # 随机返回指定个数的值,number为返回值的个数

打乱顺序

item = [3,4,5,6,7,8]
random.shuffle(item)
print(item)

练习:

生成验证码

def random_core(num, alphen=True):
    core = ''
    for i in range(num):
        if alphen:
            int = str(random.randint(0, 9))
            choice = random.choice([int])
            core += choice
        else:
            int = str(random.randint(0,9))
            s1 = chr(random.randint(65,92))
            s2 = chr(random.randint(97,122))
            choice = random.choice([int,s1,s2])
            core += choice
    return core
生成字母加数字或纯数字的验证码

发红包

# 发200块钱红包,发10个
# 把200分成10份,其中起始0和结尾200是固定不变的
# 把取到的11个数分别用后面的一个数减去前面的一个数,结果为10个数,相加的结果为200

import random

# 基础版
ret = random.sample(range(1,200),9)  # 取随机的9个数,不含0和200,相当于把200分成了10份
ret.extend([0,200])   # 把固定不变的0和200添加到生成的列表中
ret.sort()  # 从小到大排序
ret1 = []  # 定义空列表
for i in range(10):  # 循环分割的份数,取列表的索引值
    ret1.append(ret[i+1] - ret[i])  # 用后面的数减去前面的数,并添加到空列表
print(ret1)
# [49, 20, 18, 3, 16, 62, 7, 1, 15, 9]

# 进阶版
def red_packets(money,num):  # 定义两个形参,分别是钱数和个数
    ret = random.sample(range(1, money*100), num-1)  # 将元换算成分 ,并取个数减1次
    ret.extend([0, money*100])  # 把固定不变的0和200*100添加到生成的列表中
    ret.sort()  # 从小到大排序
    ret1 = []  # 定义空列表
    for i in range(num):  # 循环分割的份数,取列表的索引值
        ret1.append((ret[i + 1] - ret[i])/100) # 把分换算成元,添加到列表
    return ret1

print(red_packets(200,10))
# [40.98, 3.24, 15.62, 17.43, 6.83, 29.04, 36.09, 20.7, 0.42, 29.65]
 
# 列表表达式版
def red_packets(money,num):  # 定义两个形参,分别是钱数和个数
    ret = random.sample(range(1, money*100), num-1)  # 将元换算成分 ,并取个数减1次
    ret.extend([0, money*100])  # 把固定不变的0和200*100添加到生成的列表中
    ret.sort()  # 从小到大排序
    return [(ret[i + 1] - ret[i])/100 for i in range(num)]  # 把for循环写成一个列表表达式的形式

print(red_packets(200,10))
# [4.11, 13.39, 41.13, 16.06, 59.75, 0.69, 27.12, 5.8, 8.07, 23.88]

# 生成器版
def red_packets(money,num):  # 定义两个形参,分别是钱数和个数
    ret = random.sample(range(1, money*100), num-1)  # 将元换算成分 ,并取个数减1次
    ret.extend([0, money*100])  # 把固定不变的0和200*100添加到生成的列表中
    ret.sort()  # 从小到大排序
    for i in range(num):  # 循环分割的份数,取列表的索引值
        yield (ret[i + 1] - ret[i])/100 # 用yield关键字是函数变成生成器,每次只取一个值

for i in red_packets(200,10):
    print(i)

# 9.72
# 6.63
# 24.48
# 24.78
# 3.31
# 56.34
# 4.39
# 10.99
# 44.14
# 15.22
View Code

解释器交互模块sys

import sys

sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n)        退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version        获取Python解释程序的版本信息
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称

sys方法配置

path模块路径
print(sys.path)  # 查找模块顺序的路径
['C:\\Users\\admin\\PycharmProjects\\xu\\s22day08', 'C:\\Users\\admin\\PycharmProjects\\xu', 'F:\\python\\python36.zip', 'F:\\python\\DLLs', 'F:\\python\\lib', 'F:\\python', 'F:\\python\\lib\\site-packages']

platform 当前平台是windows,linux,mac
print(sys.platform) # 返回操作系统平台名称
win32

version python版本
print(sys.version)  # 返回python解释器版本
3.6.1rc1 (v3.6.1rc1^0:e0fbe5feee4f9c00f09eb9659c2182183036261a, Mar  4 2017, 20:00:12) [MSC v.1900 64 bit (AMD64)]

argv 返回当前文件的路径
print(sys.argv)  # 返回当前文件的路径
['C:/Users/admin/PycharmProjects/xu/s22day08/05 sys模块.py']

modules记载已经导入的模块的内存地址
print(sys.modules)
m 'F:\\python\\lib\\encodings\\__init__.py'>, 'codecs': <module 'codecs' from 'F:\\python\\lib\\codecs.py'>, '_codecs': <module '_codecs' (built-in)>, 'encodings.aliases': <module 'encodings.aliases' from 'F:\\python\\lib\\encodings\\aliases.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from 'F:\\python\\lib\\encodings\\utf_8.py'>, '_signal': <module '_signal' (built-in)>, '__main__': <module '__main__' from 'C:/Users/admin/Pycha

exit()
sys.exit(0)  # 程序退出返回状态,0为正常,1为不正常
View Code

系统模块os

import os

os.getcwd()      获取当前工作目录
os.chdir('dirname')      改变当前脚本工作目录,相当于cd
os.makedirs('dirname1/dirname2')      递归创建目录
os.removedis('dirname')    递归删除空目录
os.rmdir(dirname)          删除单个目录
os.mkdir('dirname')        创建单级目录
os.listdir('dirname')      列出指定目录下的所有文件及目录
os.remove()           删除一个文件
os.rename('oldname','newname')  文件改名
os.stat('path/filename')        查看文件信息
os.sep        输出操作系统特定的路径分割符
os.linesep    输出当前平台使用的行终止符,win下为\t\n,linux下为\t
os.pathsep    输出用于分割文件路径的字符串,win下为; Linux为:
os.name       输出当前使用平台的名字 win下为nt,linux下为posix
os.system('bash command')          运行shell命令,直接显示
os.popen('bash command').read()    运行shell命令,获取执行结果
os.environ    获取系统环境变量

path.abspath(path)    返回绝对路径
path.split(path)      将路径分割成目录和文件以元祖的形式返回
path.dirname(path)    返回路径的目录部分
path.basename(path)   返回路径的文件部分
path.exists(path)     判断路径是否存在,是True,否False
path.isabs(path)      判断是否是绝对路径,是True
path.isfile(path)     判断文件是否存在
path.isdir(path)      判断目录是否存在
path.join(path)       将多个路径组合返回
path.getatime(path)   指定文件或目录的最后访问时间
path.getmtime(path)   指定文件或目录的最后修改时间
path.getsize(path)    返回文件或目录的大小

os方法配置

getcwd()  获取当前工作目录
print(os.getcwd()) # 当前程序工作的工作目录
print(__file__)  # 输出当前目录的全路径

chdir('dirname')  # 改变当前脚本工作目录,相当于cd
os.chdir('D:/python-edit/s22day07')  # 切换工作目录
print(os.getcwd())  # 获取切换后的目录

makedirs('dirname1/dirname2')  递归创建目录
os.makedirs('dirname1/dirname2')  # 递归创建目录

removedis('dirname')  递归删除空目录
os.removedirs('dirname1/dirname2')  # 递归删除目录

mkdir('dirname')  创建单级目录
os.mkdir('dirname1')  # 创建单级目录

rmdir(dirname)      删除单个目录
os.rmdir('dirname1')  # 删除单级目录

listdir('dirname')   列出指定目录下的所有文件及目录
print(os.listdir('D:/python-edit/s22day08/'))  # 列出路径下的所有文件

remove()  删除一个文件
os.remove('D:/python-edit/s22day08/dirname1/test')  # 删除文件

rename('oldname','newname')  文件改名
os.rename('D:/python-edit/s22day08/dirname1/test','D:/python-edit/s22day08/dirname1/test1') # 文件改名

stat('path/filename')  查看文件信息
print(os.stat('D:/python-edit/s22day08/dirname1/test1'))  # 输出文件状态

sep  输出操作系统特定的路径分割符
print(os.sep)  # 输出当前平台的路径分割符

linesep  输出当前平台使用的行终止符,win下为\t\n,linux下为\t
print(os.linesep)  # 输出当前平台的换行符

pathsep   输出用于分割文件路径的字符串,win下为; Linux为:
print(os.pathsep)  # 输出当前平台的路径文件分割符(比如环境变量)

name   输出当前使用平台的名字 win下为nt,linux下为posix
print(os.name)  # 输出表示当前平台的字符串

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

popen('bash command').read() 运行shell命令,获取执行结果
print(os.popen('dir').read())  # 运行shell命令,显示结果

environ   获取系统环境变量
print(os.environ)  # 以字典的形式输出当前系统的环境变量

path.abspath(path)   返回绝对路径
print(os.path.abspath('D:/python-edit/s22day08/06 os模块.py'))  # 返回绝对路径

path.split(path)  将路径分割成目录和文件以元祖的形式返回
print(os.path.split('D:/python-edit/s22day08/06 os模块.py'))  # 将路径切割成两部分,第一部分是目录,第二部分为文件

path.dirname(path)  返回路径的目录部分
print(os.path.dirname('D:/python-edit/s22day08/06 os模块.py'))  # 返回路径的目录部分

path.basename(path)  返回路径的文件部分
print(os.path.basename('D:/python-edit/s22day08/06 os模块.py'))  # 返回路径的文件部分

path.exists(path) 判断路径是否存在,是True,否False
print(os.path.exists('D:/python-edit/s22day08/06 os模块.py'))  # 判断路径是否存在

path.isabs(path)  判断是否是绝对路径,是True
print(os.path.isabs('D:/python-edit/s22day08/06 os模块.py')) # 判断是不是绝对路径

path.isfile(path)  判断文件是否存在
print(os.path.isfile('06 os模块.py'))  # 判断是否是文件

path.isdir(path)  判断目录是否存在
print(os.path.isdir('D:/python-edit/s22day08/'))  # 判断是否是目录

path.join(path)  将多个路径组合返回
print(os.path.join('D:/python-edit/s22day08/','s22day07','s22day06'))  # 路径拼接并返回

path.getatime(path)  指定文件或目录的最后访问时间
print(os.path.getatime('06 os模块.py'))  # 返回文件最后访问时间

path.getmtime(path)  指定文件或目录的最后修改时间
print(os.path.getmtime('06 os模块.py'))  # 返回文件最后修改时间

path.getsize(path)  返回文件或目录的大小
print(os.path.getsize('06 os模块.py')) # 查看文件或目录的大小
print(os.path.getsize('01 常用模块.py'))
print(os.path.getsize('02 collections模块.py'))
print(os.path.getsize('03 time模块.py'))
print(os.path.getsize('dirname1/test1'))
View Code

练习题:计算文件大小

import os
path = os.path.dirname(__file__)  # 获取当前路径
print(path) # C:/Users/admin/PycharmProjects/xu/s22day09
def get_size(path):
    ret = os.listdir(path) # 列出当前路径下所有的文件或目录
    total = 0
    for name in ret:
        result = os.path.join(path,name)  # 将当前目录下的文件或目录和当前路径进行拼接
        if os.path.isdir(result):  # 判断,如果当前路径是一个目录的话
            total += get_size(result)  # 递归计算目录下面的所有文件的大小
        else:
            total += os.path.getsize(result)  # 反之,直接计算文件大小
    return total 

print(get_size(path))
递归方式
import os
path = os.path.dirname(__file__)  # 获取当前路径
path_lst = [path]  # 把当前路径放到一个列表中
total = 0
while path_lst:
    ret = path_lst.pop()   # 只去列表的最后一个值
    ret1 = os.listdir(ret)  # 看取到的路径下的所有内容
    for name in ret1:
        ret2 = os.path.join(ret,name)  # 路径拼接
        if os.path.isdir(ret2):
            path_lst.append(ret2)  # 判断,如果是目录,则加到列表中
        else:
            total += os.path.getsize(ret2)  # 如果是文件,则计算
print(total)
堆栈版

正则模块re

import re

正则表达式和python语言不发生关系,是一个独立的语法

正则规则:

     1、从一个巨大的字符串集合中,根据规则来找到你想要的内容,

     2、或者判断某一段字符串是否是你规定的

[] 字符组
.  通配符
*  匹配0到+00
+  匹配1到+00
?  匹配0或1
{} 匹配指定个数
^  匹配以什么开头的字符
$  匹配以什么结尾的字符
|   或,如 a|b 表示匹配a或者b
()  匹配括号内的表达式
[^]  匹配除什么以外的所有字符
[-]  匹配一个范围,比如[0-9A-z]
[\]  转义
\d  匹配数字
\w  匹配字母数字或下划线
\s  匹配任意空白字符
\D  匹配非数字
\W  匹配非字母数字下划线
\S  匹配非空白字符
\n  匹配换行符
\t  匹配制表符
\b  匹配一个单词的结尾

字符组[ ]:

数字 [0-9]  匹配0-9任意一个数字
print(re.findall('[0-9]','a1b2c3e4f5g6')) # 以列表的形式输出,结果['1', '2', '3', '4', '5', '6']

字母 [A-z] 匹配大A到小z任意一个字母
print(re.findall('[A-z]','ds0AfsB98dM7d8da')) # 匹配到了除数字以外所有字符,结果['d', 's', 'A', 'f', 's', 'B', 'd', 'M', 'd', 'd', 'a']

数字和字母 [A-Za-z0-9]  匹配大A-大Z任意一个字母,小a-小z任意一个字母,0-9任意一个数字
print(re.findall('[A-Za-z0-9]','H1ellO23 Wor90l3d'))  # 匹配所有字母和数字,结果['H', '1', 'e', 'l', 'l', 'O', '2', '3', 'W', 'o', 'r', '9', '0', 'l', '3', 'd']
View Code

元字符 (有特殊功能的符号)

通配符 . 匹配任意一个除换行符以(\n)外的所有字符
print(re.findall('a.c','abcafc'))  # 匹配单一字符,想匹配几个就写几个 . 结果['abc', 'afc']

*  匹配前面字符0个-无穷个
print(re.findall('h.*','hello world'))  # 匹配除h外任意字符0-无穷个,结果['hello world']

+  匹配前面字符1个-无穷个
print(re.findall('he+','hello world'))  # 匹配字母e有1-无穷个 结果['he']

?  匹配前面字符0个或1个
print(re.findall('he?','hello world'))  # 匹配字母e有0个或1个,匹配不成功,返回[]

{n}  匹配前面字符n个
print(re.findall('h.l{2}','hello world'))  # 匹配l必须有两个才成功 结果['hell']

{n,} 匹配前面字符n个-无穷个
print(re.findall('h.l{0,}','hellllllo world'))  # 匹配l有0个-无穷个,和*的意思一样 结果['hellllll']

{n,m}  匹配前面的字符n个-m个
print(re.findall('h.l{0,3}','hellllllo world'))  # 匹配l有0个或3个 结果['helll']

() 分组 ,只匹配分组里面的内容,优先级高
print(re.findall('(ab)+.h','abab.hello')) # ['ab']

?:  取消分组的优先级
print(re.findall('(?:ab)+.h','abab.hello')) # ['abab.h']

| 或,匹配|左右两边的字符
print(re.findall(r'(xyy|xyp)','xyy和xypppp'))  # ['xyy', 'xyp']
print(re.findall(r'[xyy|xyp]+','xyscyday和xdsdypppp'))  # ['xy', 'y', 'y', 'x', 'ypppp']

^  以什么开头
print(re.findall('^AAA','AAAbcd123L'))  # 匹配以AAA开头的字符,结果['AAA']

$  以什么结束
print(re.findall('Lmn$','AAAbcd123Lmn'))  # 匹配以Lmn结尾的字符,结果['Lmn']
print(re.findall('^h.*d$','hello world'))  # 匹配以h开头,中间有任意字符0-无穷个,再以d结尾的字母,结果['hello world']
print(re.findall('^h.+d$','hello world'))  # 匹配以h开头,中间有任意字符1-无穷个,再以d结尾的字母,结果['hello world']
print(re.findall('^h.?d$','hello world'))  # 匹配以h开头,中间有任意字符0-1个,再以d结尾的字母,结果[]
 
View Code

字符组内含有特殊意义的字符

[^]  匹配非指定字符外所有的字符
print(re.findall('[^0-9]+','H1ellO23 Wor90l3d'))  # 匹配非0-9任意数字外的所有字符,结果['H', 'ellO', ' Wor', 'l', 'd']

[-]  匹配一个范围内的单个字符
print(re.findall('[0-9]+','H1ellO23 Wor90l3d456'))   # 匹配0-9任意一个数字,以列表的形式返回,结果['1', '23', '90', '3', '456']

[\]  转义
  # 反斜杠后面接元字符去除特殊功能
  # 反斜杠后面接普通字符变成特殊符号
print(re.findall('-?\d+\.?\d*(?:\*|\/)-?\d+\.?\d*','-2*3+6*3.5-24/-2+10*-8'))
View Code

字符

\w 匹配数字字母或下划线,别的符号不匹配
print(re.findall(r'\w+','abc123__12www//')) # ['a', 'b', 'c', '1', '2', '3', '_', '_', '1', '2']

\s 匹配任意的空白字符
print(re.findall(r'\s','hello world ')) # [' ', ' ']

\d 匹配数字
print(re.findall(r'\d','abc123heelo python222')) # ['1', '2', '3', '2', '2', '2']

\n 匹配换行符\n
print(re.findall(r'\n','\t\ndad123')) # ['\n']

\t 匹配制表符
print(re.findall(r'\t','dasd\tdas\t\n')) # ['\t', '\t']

\b
print(re.findall(r'\b','hello world'))

\W 匹配非字母数字下划线的字符
print(re.findall(r'\W','abc123__12www//')) # ['/', '/']

\S 匹配非空白字符
print(re.findall('\S+','hello world')) # ['hello', 'world']

\D 匹配非数字的字符
print(re.findall('\D+','abc123heelo python222')) # ['abc', 'heelo python']

() 分组 ,只匹配分组里面的内容,优先级高
print(re.findall('(ab)+.h','abab.hello')) # ['ab']

?: 取消分组的优先级
print(re.findall('(?:ab)+.h','abab.hello')) # ['abab.h']

| 或,匹配|左右两边的字符
print(re.findall(r'(xyy|xyp)','xyy和xypppp')) # ['xyy', 'xyp']

print(re.findall(r'[xyy|xyp]+','xyscyday和xdsdypppp')) # ['xy', 'y', 'y', 'x', 'ypppp']
View Code

re模块方法:

findall 取所有,返回列表,对分组有一个优先的显示
search 取匹配的第一个值,返回对象,通过group取值
match 取以正则规则开头的第一个对象,通过group取值
sub  把正则规则替换成想替换的值
subn   返回替换个次数
compile 编译正则规则,返回正则表达式的对象,可以任意匹配想用的方法如:findall,search,match等
finditer 把结果以迭代器的形式返回,然后group取值

具体示例:

import re
import os

match
ret = re.match('\d+','34ad90vs18')
print(ret.group())

print(os.environ)
split 以正则规则去分割字符串,返回列表,如果加上分组的话,分割后会被保留分割符
print(re.split(';','D:\python36\Scripts\;D:\python36\;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell'))
['D:\\python36\\Scripts\\', 'D:\\python36\\', '%SystemRoot%\\system32', '%SystemRoot%', '%SystemRoot%\\System32\\Wbem', '%SYSTEMROOT%\\System32\\WindowsPowerShell']

sub 把正则规则替换成想替换的值
print(re.sub('\d+','|','(we1 are2 family3)',2))

subn 返回替换个次数
print(re.subn('\d+','|','(we1 are2 family3)'))

compile 编译正则规则,返回正则表达式的对象,可以任意匹配想用的方法如:findall,search,match等
obj = re.compile('\d{3}') #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串
print(ret.group())

finditer 把结果以迭代器的形式返回,然后group取值
ret = re.finditer('\d', 'ds3sy4784a')
print(ret)
for i in ret:
    print(i.group())
View Code

计算器

# 计算1 - 2 * ( (60-30 +(-40/5)* (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )的值,利用正则模块
import re
# 程序总入口
def calculator():
    calculator = "1 - 2 * ( (60-30 +(-40/5)* (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )".replace(' ','')  # 去除字符串空格
    while True:
        if '(' in calculator:
            call = re.search('\([^()]+\)',calculator)  # 匹配括号且括号里面没有括号的字符
            if call:
                each_value = call.group()
                if '*' in each_value:
                    value1 = each_value.split('*')
                    if '/' in value1[0]:
                        value = division(each_value)
                    else:
                        value = multiplication(each_value)
                elif '/' in each_value:
                    value = division(each_value)
                else:
                    value = addition(each_value)
                calculator = calculator.replace(each_value,str(value))  # 计算完加减法后,将匹配到的括号替换为最后计算的值
        elif '(' not in calculator:
            value = multiplication(calculator)
            value = addition(value)
            print(value)
            break

# 定义一个除法函数,用于除法计算
def division(formula):
    formula1 = re.search('\d+\.?\d*/-?\d+\.?\d*',formula)
    if formula1:
        f = formula1.group()
        l1 = f.split('/')
        value1 = float(l1[0])/float(l1[1])
        value = formula.replace(f, str(value1))  # 按顺序把字符串中匹配到的值替换成计算后的值
        return value

# 定义乘法函数,用于乘法计算
def multiplication(formula):
    formula1 = re.search('\d+\.?\d*\*-?\d+\.?\d*',formula)
    if formula1:
        f = formula1.group()
        l1 = f.split('*')
        value1 = float(l1[0])*float(l1[1])
        value = formula.replace(f,str(value1))  # 按顺序把字符串中匹配到的值替换成计算后的值
        return value

# 定义加法函数,用于加减法计算
def addition(formula):
    if '--' in formula:
        formula = formula.replace('--','+')  # 把字符串中出现的--替换成+
    formula1 = re.findall('-?\d+\.?\d*', formula)  # 按顺序把字符串中匹配到的值替换成计算后的值
    value = 0
    for i in formula1:
        value += float(i)
    return value


calculator()
View Code

日志模块logging

简单配置:

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

默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。

灵活配置日志级别,日志格式,输出位置:

logging.basicConfig(level=logging.DEBUG,  # 设置日志级别
            format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',  # 设置日志输出格式
            datefmt='%a, %d %b %Y %H:%M:%S', # 设置日志显示时间
            filename='test.log',   # 设置日志存放文件
            filemode='w')   # 设置日志对文件的操作模式
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')

配置参数详解:

logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:

filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。

format参数中可能用到的格式化串:
%(name)s Logger的名字
%(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用户输出的消息
View Code

常用配置:

logger= logging.getLogger()
fh = logging.FileHandler(settings.log_path,encoding='utf-8')
sh = logging.StreamHandler()

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

sh.setFormatter(fomatter)
fh.setFormatter(fomatter)

logger.addHandler(sh)
logger.addHandler(fh)
logger.setLevel(logging.DEBUG)
fh.setLevel(logging.WARNING)  # 单独设置文件存储日志级别

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

logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,可以通过:logger.setLevel(logging.Debug)设置级别,当然,也可以通过fh.setLevel(logging.WARNING) 单独设置文件输出级别

hashlib模块

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

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

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

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

我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:

import hashlib

ret1 = hashlib.md5()  # 调用MD5加密算法,生成一个对象
ret1.update('pwd@123'.encode('utf-8'))   # 通过这个对象调用hashlib内置方法update加密一个字符串
ret2 = ret1.hexdigest()   # 获取加密后的字符串
print(ret2)
# b757f97c9ff375ae14b1260e690fb210

MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。

另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:

ret1 = hashlib.sha1()  # 调用sha1加密算法,生成一个对象
ret1.update('pwd@123'.encode('utf-8'))   # 通过这个对象调用hashlib内置方法update加密一个字符串
ret2 = ret1.hexdigest()   # 获取加密后的字符串
print(ret2)
# abf20e4b22785fad3f0d7c7014a36daa3539df92

SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,而且摘要长度更长。

摘要算法应用

任何允许用户登录的网站都会存储用户登录的用户名和口令。如何存储用户名和口令呢?方法是存到数据库表中:

name    | password
--------+----------
michael | 123456
bob     | abc999
alice   | alice2008

如果以明文保存用户口令,如果数据库泄露,所有用户的口令就落入黑客的手里。此外,网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,比如MD5:

username | password
---------+---------------------------------
michael  | e10adc3949ba59abbe56e057f20f883e
bob      | 878ef96e86145580c38c87f0410ad153
alice    | 99b1c2188db85afee403b1536010c2c9

考虑这么个情况,很多用户喜欢用123456,888888,password这些简单的口令,于是,黑客可以事先计算出这些常用口令的MD5值,得到一个反推表:

'e10adc3949ba59abbe56e057f20f883e': '123456'
'21218cca77804d2ba1922c33e0151105': '888888'
'5f4dcc3b5aa765d61d8327deb882cf99': 'password'

这样,无需破解,只需要对比数据库的MD5,黑客就获得了使用常用口令的用户账号。

对于用户来讲,当然不要使用过于简单的口令。但是,我们能否在程序设计上对简单口令加强保护呢?

由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:

hashlib.md5("salt".encode("utf8"))

经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。

但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?

如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。

摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

练习:

生成文件的MD5值:

简版:

def hash_md5(file):
with open(file,encoding='utf-8') as f1:
line = f1.read()
md5_obj = hashlib.md5()
md5_obj.update(line.encode('utf-8'))
return md5_obj.hexdigest()

print(hash_md5('README'))
View Code

完整版:

import os
import hashlib
def get_md5(file,size=10240):
with open(file,'rb') as f1:
md5_obj = hashlib.sha1()
file_size = os.path.getsize(file)
while file_size > 0:
md5_obj.update(f1.read(size))
file_size -= size
return md5_obj.hexdigest()

# f2ac4e0a12acb9c079d21d8650ded2a07a7eb9a2
print(get_md5('README'))
View Code

序列化模块json和pickle

json

1、什么是序列?
一个有序的结构,列表,元祖,字符串

2、什么是序列化?
数据结构转变成字符串的过程叫做序列化

3、反序列化
字符串转换成数据结构的过程叫做反序列化

4、为什么要序列化?
  1、文件存储
  2、网络传输

5、序列化的目的

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

import json

dump
load
dumps 将数据类型转字符串
loads 反序列化过程

序列化
ret1 = json.dumps([1,2,3])
print(repr(ret1))
# '[1, 2, 3]'

ret2 = json.dumps({'name':'alex','sex':'male',88:'in'})
print(repr(ret2))
# '{"name": "alex", "sex": "male", "88": "in"}' # 字典中的key是数字的话,被强转成了字符串

反序列化
ret3 = json.loads(ret1)
print(repr(ret3))
# [1, 2, 3]

ret4 = json.loads(ret2)
print(repr(ret4))
# {'name': 'alex', 'sex': 'male', '88': 'in'} 反序列化回来后key还同样是字符串
dump和loads
user = input('user:').strip()
f = open('info.txt','w')
ret1 = json.dump(user,f)
f.close()

f = open('info.txt','r')
ret1 = json.load(f)
f.close()
print(ret1)
dump和load
user = input('user: ').strip()
pwd = input('pwd: ').strip()
userinfo = {}
f = open('info.txt','a',encoding='utf-8')
userinfo['username'] = user
userinfo['password'] = pwd
ret = json.dumps(userinfo) # 将字典序列化成字符串格式
f.write(ret + '\n')  # 将序列化后的字典写入到文件
f.close()


f = open('info.txt','r',encoding='utf-8')
for line in f:   # 先按行把文件内容读取出来
    ret1 = json.loads(line)  # 再把读取出来的内容反序列化
    print(ret1,type(ret1))   # {'username': 'zhangsan', 'password': '123123'} <class 'dict'>
f.close()
用dumps和loads操作文件

总结:
json中,字典里的key只能是字符串
json只支持有限的数据类型,比如,字典,列表,数字类型
json中只识别双引号

pickle

import pickle

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

dic = {'name':'alex','sex':'male',88:'in'}

f = open('info.txt','ab')
ret1 = pickle.dump(dic,f)
f.close()

f = open('info.txt','rb')
while True:
try:
print(pickle.load(f))
except EOFError:
break

f.close()

class Person:
def __init__(self,name,age):
self.name = name
self.age = age

p = Person('eric',99)

with open('info.txt','wb') as f1:
pickle.dump(p,f1)

with open('info.txt','rb') as f1:
ret = pickle.load(f1)
print(ret)
print(ret.name,ret.age)
View Code

json和pickle的区别:
json
1、结果可读
2、所有的语言都通用
3、数据类型有限

pickle
1、结果不可读
2、只支持pytho语言
3、几乎支持所有数据类型

posted on 2018-07-05 19:42  花豆豆  阅读(210)  评论(0编辑  收藏  举报