Python常用模块
time模块(时间)
import time
时间戳
时间戳(timestamp):时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。
import time
time_stamp = time.time()
print(time_stamp,type(time_stamp))
1690376372.7548702 <class 'float'>
格式化时间
格式化的时间字符串(format string):格式化时间表示的是普通的字符串格式的时间。
import time
format_time = time.strftime("%Y-%m-%d %X")
print(format_time,type(format_time))
2023-07-26 21:04:03 <class 'str'>
“%Y-%m-%d %X"是时间格式字符串的格式指令。
其中,”%Y"表示年份,“%m"表示月份,”%d"表示日期,"%X"表示时间(小时:分钟:秒)。
time.strftime("%Y-%m-%d %X")会返回一个字符串,表示当前时间按照指定格式进行格式化后的结果。
在这段代码中,format_time接收了格式化后的时间字符串,并通过print函数打印输出。type(format_time)用于获取format_time的数据类型,并将结果也打印输出。
结构化时间
结构化的时间(struct time):struct_time元组共有9个元素共九个元素,分别为(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)
import time
print('本地时区的struct_time:\n{}'.format(time.localtime()))
print('UTC时区的struct_time:\n{}'.format(time.gmtime()))
本地时区的struct_time:
time.struct_time(tm_year=2023, tm_mon=7, tm_mday=27, tm_hour=10, tm_min=29, tm_sec=17, tm_wday=3, tm_yday=208, tm_isdst=0)
UTC时区的struct_time:
time.struct_time(tm_year=2023, tm_mon=7, tm_mday=27, tm_hour=2, tm_min=29, tm_sec=17, tm_wday=3, tm_yday=208, tm_isdst=0)
使用time模块中的localtime和gmtime函数分别获取本地时区和UTC时区的当前时间。
time.localtime()函数返回当前本地时区的时间,其返回值是一个struct_time对象,可以包含年、月、日、小时、分钟、秒等时间信息。
time.gmtime()函数返回当前的UTC时区的时间,其返回值也是一个struct_time对象,包含了相同的时间信息,只是时区不同。
使用format()方法,通过字符串中的占位符{}将获取到的struct_time对象插入到字符串中进行格式化输出。
因此,代码的输出结果为本地时区的struct_time对象和UTC时区的struct_time对象的字符串表示。
# 结构化时间的基准时间
print(time.localtime(0))
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=8, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)
在Python中,结构化时间的基准时间是指从1970年1月1日零时(UTC)开始计算的秒数,也被称为UNIX时间戳。在time.localtime()函数中,通过将参数设为0,表示获取基准时间对应的本地时区的struct_time对象。
因此,print(time.localtime(0))的输出结果是基准时间对应的本地时区的struct_time对象的表示,包含年、月、日、小时、分钟、秒等时间信息。这个时间通常被称为"Epoch time"或"Unix time",是计算机系统中常用的时间表示方式。
# 结构化时间的基准时间上增加一年时间
print(time.localtime(3600*24*365))
time.struct_time(tm_year=1971, tm_mon=1, tm_mday=1, tm_hour=8, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=1, tm_isdst=0)
在Python中,结构化时间的基准时间是从1970年1月1日零时(UTC)开始计算的秒数,也被称为UNIX时间戳。如果想要在基准时间上增加一年的时间,可以通过将秒数为一年的总秒数(365天 * 24小时 * 60分钟 * 60秒)加到基准时间上。
在time.localtime()函数中,可以通过将参数设为一年的总秒数(3600 * 24 * 365)来获取增加一年后的本地时区的struct_time对象。
因此,print(time.localtime(360024365))的输出结果是基准时间上增加一年后对应的本地时区的struct_time对象的表示,包含年、月、日、小时、分钟、秒等时间信息。
不同格式时间的转换
如上图所示,我们总能通过某些方法在结构化时间-格式化时间-时间戳三者之间进行转换
- 结构化时间(struct_time):
- 结构化时间是一种元组对象,用于保存时间的各个组成部分(年、月、日、时、分、秒等)。可以使用time.localtime()函数获取当前的本地结构化时间,或使用time.gmtime()函数获取当前的UTC结构化时间。也可以通过time.struct_time类中的方法和属性来操作和访问结构化时间的各个组成部分。
- 格式化时间(字符串表示):
- 格式化时间是将结构化时间通过特定的格式指令转换为字符串表示的时间。可以使用time.strftime()函数来实现。该函数接受两个参数:格式字符串和结构化时间对象。格式字符串指定时间的输出格式,包含各种格式指令,如%Y表示年份,%m表示月份,%d表示日期,等等。
- 时间戳(浮点数表示):
- 时间戳是以秒为单位表示时间的浮点数值。可以使用time.time()函数获取当前的时间戳,即从1970年1月1日零时(UTC)开始经过的秒数。也可以使用time.mktime()函数将给定的结构化时间对象转换为时间戳表示。
下面我们将用代码展示如何通过这些方法转换时间格式。
# 结构化时间
now_time = time.localtime()
print(now_time)
time.struct_time(tm_year=2023, tm_mon=7, tm_mday=27, tm_hour=10, tm_min=57, tm_sec=0, tm_wday=3, tm_yday=208, tm_isdst=0)
# 把结构化时间转换为时间戳格式
print(time.mktime(now_time))
1690426620.0
#把结构化时间转换为格式化时间
#%Y年-%m月-%d天 %X时分秒=%H时:%M分:%S秒
print(time.strftime("%Y-%m-%d %X",now_time))
2023-07-27 10:57:00
# 把格式化时间转换为结构化时间,它和strftime()是逆操作
print(time.strptime('2023-07-27 11:10:00','%Y-%m-%d %X'))
time.struct_time(tm_year=2023, tm_mon=7, tm_mday=27, tm_hour=11, tm_min=10, tm_sec=0, tm_wday=3, tm_yday=208, tm_isdst=-1)
# 把结构化时间表示为这种形式:'Sun Jun 20 23:21:05 1993'。
print(time.asctime())
Thu Jul 27 11:27:35 2023
# 如果没有参数,将会将time.localtime()作为参数传入。
print(time.asctime(time.localtime()))
Thu Jul 27 11:33:53 2023
# 把一个时间戳转化为time.asctime()的形式。
#time.ctime()函数用于将给定的时间戳(以秒为单位)或结构化时间对象转换为特定格式的字符串表示。如果不传递参数,则默认使用当前的时间戳。
print(time.ctime())
Thu Jul 27 11:36:13 2023
# 如果参数未给或者为None的时候,将会默认time.time()为参数。它的作用相当于time.asctime(time.localtime(secs))。
print(time.ctime(time.time()))
Thu Jul 27 11:36:49 2023
其他用法
# 推迟指定的时间运行,单位为秒
start = time.time()
time.sleep(3)
end = time.time()
print(end-start)
3.0029077529907227
使用了time.sleep()函数来推迟指定的时间运行。该函数接受一个参数,表示需要推迟的时间(以秒为单位)。
在上述示例中,使用time.sleep(3)将程序推迟运行3秒钟。然后,通过time.time()函数获取当前的时间戳,将其分别赋值给start和end变量。最后,计算时间延迟的秒数并打印出来。
运行以上代码,输出结果应该接近3,表示经过了大约3秒的延迟。
需要注意的是,time.sleep()函数会阻塞当前线程的执行,以达到推迟运行的效果。在实际应用中,可以使用time.sleep()函数进行短暂延迟,以模拟等待、定时任务、限制速率等场景。
datetime模块(时间加减模块)
# datetime模块可以看成是时间加减的模块
import datetime
# 返回当前时间
print(datetime.datetime.now())
2023-07-27 11:40:18.744859
print(datetime.date.fromtimestamp(time.time()))
2023-07-27
datetime.date.fromtimestamp()函数来将给定的时间戳转换为对应的日期对象。
在上述示例中,你使用time.time()函数获取当前的时间戳,然后将其作为参数传递给datetime.date.fromtimestamp()函数。该函数将时间戳转换为对应的日期对象。
运行以上代码,会输出当前日期的字符串表示,例如2023-07-26。
# 当前时间+3天
print(datetime.datetime.now() + datetime.timedelta(3))
2023-07-30 11:47:25.088977
# 当前时间-3天
print(datetime.datetime.now() + datetime.timedelta(-3))
2023-07-24 11:48:13.516471
# 当前时间-3小时
print(datetime.datetime.now() + datetime.timedelta(hours=3))
2023-07-27 14:50:03.179462
# 当前时间+30分钟
print(datetime.datetime.now() + datetime.timedelta(minutes=30))
2023-07-27 12:21:37.338745
# 时间替换
c_time = datetime.datetime.now()
print(c_time.replace(minute=20,hour=5,second=13))
2023-07-27 05:20:13.563436
首先使用datetime.datetime.now()获取当前的日期和时间的datetime对象,并将其赋值给变量c_time。然后,通过replace()方法,传入需要修改的分钟、小时和秒钟的值,来创建一个新的datetime对象,表示在当前日期的基础上修改了指定的时间。
当前时间是2023年7月26日 10:34:14,那么输出结果将是同一天的5:20:13。
random模块(随机)
import random
# 大于0且小于1之间的小数
print(random.random())
0.4590044316266283
在Python中,random.random()函数是random模块提供的一个方法,用于生成一个范围在0到1之间的随机小数。该函数返回一个浮点数,取值范围包括0(含)和1(不含)。
# 大于等于1且小于等于3之间的整数
import random
print(random.randint(1,3))
2
在Python中,random.randint(a, b)函数是random模块提供的方法之一,用于生成一个范围在a到b之间(包含a和b)的随机整数。该函数返回一个整数。
#大于等于1且小于3之间的整数
print(random.randrange(1,3))
1
在Python中,random.randrange(start, stop[, step])函数是random模块提供的方法之一,用于生成一个范围在start到stop之间但不包含stop的随机整数。该函数返回一个整数。
# 大于1小于3的小数,如1.4156161644
print(random.uniform(1,3))
2.491664208276728
在Python中,random.uniform(a, b)函数是random模块提供的方法之一,用于生成一个范围在a到b之间的随机小数。该函数返回一个浮点数。
# 列表内的任意一个元素,即1或者‘23’或者[4,5]
print(random.choice([1,'23',[4,5]]))
23
在Python中,random.choice(seq)函数是random模块提供的方法之一,用于从可迭代对象seq中随机选择一个元素。该函数返回所选元素。
# random.sample([], n),列表元素任意n个元素的组合,示例n=2
print(random.sample([1,'23',[4,5]],2))
['23', 1]
在Python中,random.sample(population, k)函数是random模块提供的方法之一,用于从一个序列或集合中随机选择不重复的k个元素作为样本。该函数返回一个包含所选元素的列表。
lis = [1,3,5,7,9]
random.shuffle(lis)
print(lis)
[9, 7, 3, 1, 5]
在Python中,random.shuffle(seq)函数是random模块提供的方法之一,用于随机打乱给定序列seq中的元素顺序。它会直接修改原来的序列。
os模块(文件处理模块)
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命令,直接显示
- os.environ 获取系统环境变量
- 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的大小
sys模块(操控环境)
sys模块负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python的运行时环境。
- sys.argv 命令行参数List,第一个元素是程序本身路径
- sys.modules.keys() 返回所有已经导入的模块列表
- sys.exc_info() 获取当前正在处理的异常类,exc_type、exc_value、exc_traceback当前处理的异常详细信息
- sys.exit(n) 退出程序,正常退出时exit(0)
- sys.hexversion 获取Python解释程序的版本值,16进制格式如:0x020403F0
- sys.version 获取Python解释程序的版本信息
- sys.maxint 最大的Int值
- sys.maxunicode 最大的Unicode值
- sys.modules 返回系统导入的模块字段,key是模块名,value是模块
- sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
- sys.platform 返回操作系统平台名称
- sys.stdout 标准输出
- sys.stdin 标准输入
- sys.stderr 错误输出
- sys.exc_clear() 用来清除当前线程所出现的当前的或最近的错误信息
- sys.exec_prefix 返回平台独立的python文件安装的位置
- sys.byteorder 本地字节规则的指示器,big-endian平台的值是'big',little-endian平台的值是'little'
- sys.copyright 记录python版权相关的东西
- sys.api_version 解释器的C的API版本
json和pickle模块(序列化)
序列化
把对象(变量)从内存中变成可存储或传输的过程称之为序列化, 在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening。
序列化的优点:
- 持久保存状态:内存是无法永久保存数据的,当程序运行了一段时间,我们断电或者重启程序,内存中关于这个程序的之前一段时间的数据(有结构)都被清空了。但是在断电或重启程序之前将程序当前内存中所有的数据都保存下来(保存到文件中),以便于下次程序执行能够从文件中载入之前的数据,然后继续执行,这就是序列化。
- 跨平台数据交互:序列化时不仅可以把序列化后的内容写入磁盘,还可以通过网络传输到别的机器上,如果收发的双方约定好实用一种序列化的格式,那么便打破了平台/语言差异化带来的限制,实现了跨平台数据交互。
json
Json序列化并不是python独有的,json序列化在java等语言中也会涉及到,因此使用json序列化能够达到跨平台传输数据的目的。
json数据类型和python数据类型对应关系表
Json类型 | Python类型 |
---|---|
{} | dict |
[] | list |
"string" | str |
520.13 | int或float |
true/false | True/False |
null | None |
json模块序列化和反序列化的一个过程如下图所示
import json
struct_data = {'name': 'json', 'age': 23, 'sex': 'male'}
print(struct_data,type(struct_data))
{'name': 'json', 'age': 23, 'sex': 'male'} <class 'dict'>
data = json.dumps(struct_data)
print(data,type(data))
{"name": "json", "age": 23, "sex": "male"} <class 'str'>
json.dumps(struct_data)函数将struct_data字典转换为一个JSON格式的字符串
通过将Python对象转换为JSON字符串,可以方便地将数据序列化并在不同的系统之间进行传输或存储。
data = json.loads(data)
print(data, type(data))
{'name': 'json', 'age': 23, 'sex': 'male'} <class 'dict'>
注意:无论数据是怎样创建的,只要满足json格式(如果是字典,则字典内元素都是双引号),就可以json.loads出来,不一定非要dumps的数据才能loads
在Python中,json.loads(s)是json模块提供的方法之一,用于将JSON格式的字符串s解析为相应的Python对象。
# 序列化
with open('Json序列化对象.json','w') as fw:
json.dump(struct_data,fw)
使用了json.dump(struct_data, fw)来将struct_data字典序列化为JSON格式并写入到一个名为"Json序列化对象.json"的文件中。
在Python中,json.dump(obj, file)是json模块提供的方法之一,用于将Python对象obj序列化为JSON格式,并写入到文件对象file中。
在你的代码中,json.dump(struct_data, fw)将字典struct_data序列化为JSON格式,并使用文件对象fw将序列化后的数据写入到"Json序列化对象.json"文件中。文件模式使用的是写入模式('w'),意味着如果文件已经存在,它将被覆盖;如果文件不存在,则会创建该文件。
通过这种方式,你可以将Python对象以JSON格式保存到文件中,以便后续读取和使用。
需要注意的是,在使用完文件对象后,应该及时关闭文件。你使用了上下文管理器(with open(...) as fw:),它会在代码块执行完毕后自动关闭文件。
这个代码片段会在当前目录下创建一个名为"Json序列化对象.json"的文件,并将字典struct_data序列化为JSON格式后写入该文件中。
# 反序列化
import json
with open('Json序列化对象.json') as fr:
data = json.load(fr)
print(data)
{'name': 'json', 'age': 23, 'sex': 'male'}
使用了json.load(fr)来从名为"Json序列化对象.json"的文件中读取JSON数据,并将其反序列化为Python对象,并将结果赋值给data变量。
在Python中,json.load(file)是json模块提供的方法之一,用于从文件对象file中读取JSON数据,并将其反序列化为相应的Python对象。
在你的代码中,json.load(fr)从文件对象fr中读取JSON数据,并将其反序列化为Python对象。
通过这种方式,你可以从文件中读取之前保存的JSON数据,并将其还原为相应的Python对象,以便后续使用。
需要注意的是,在读取完文件数据后,应该及时关闭文件。你的代码没有手动关闭文件,但使用了上下文管理器(with open(...) as fr:),它会在代码块执行完毕后自动关闭文件。
这个代码片段会读取名为"Json序列化对象.json"的文件中的JSON数据,并将其反序列化为相应的Python对象,然后将结果输出。
pickle
Pickle序列化和所有其他编程语言特有的序列化问题一样,它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,即不能成功地反序列化也没关系。但是pickle的好处是可以存储Python中的所有的数据类型,包括对象,而json不可以。
pickle模块序列化和反序列化的过程如下图所示
import pickle
struct_data = {'name': 'json', 'age': 23, 'sex': 'male'}
print(struct_data,type(struct_data))
{'name': 'json', 'age': 23, 'sex': 'male'} <class 'dict'>
data=pickle.dumps(struct_data)
print(data,type(data))
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x04\x00\x00\x00jsonq\x02X\x03\x00\x00\x00ageq\x03K\x17X\x03\x00\x00\x00sexq\x04X\x04\x00\x00\x00maleq\x05u.' <class 'bytes'>
在Python中,pickle.dumps(obj)是pickle模块提供的方法之一,用于将Python对象obj序列化为二进制数据。
data = pickle.loads(data)
print(data,type(data))
{'name': 'json', 'age': 23, 'sex': 'male'} <class 'dict'>
在Python中,pickle.loads(data)是pickle模块提供的方法之一,用于将二进制数据data反序列化为相应的Python对象。
pickle.loads(data)将二进制数据data反序列化为Python对象,并将结果赋值给data变量。
# 序列化(注意:pickle模块需要使用二进制存储,即'wb'模式存储)
with open('Pickle序列化对象.pkl', 'wb') as fw:
pickle.dump(struct_data,fw)
使用了pickle.dump(struct_data, fw)将字典结构体struct_data进行序列化,并将结果以二进制形式写入文件"Pick序列化对象.pkl"中。
在Python中,pickle.dump(obj, file)是pickle模块提供的方法之一,用于将Python对象obj序列化为二进制数据,并将结果写入文件对象file中。
# 反序列化
with open('Pickle序列化对象.pkl','rb') as fr:
pickle = pickle.load(fr)
print(data)
{'name': 'json', 'age': 23, 'sex': 'male'}
使用了pickle.load(fr)从文件"Pick序列化对象.pkl"中读取序列化的数据,并进行反序列化操作,将数据恢复为Python对象。然后使用print(data)语句打印输出反序列化后的Python对象。
在Python中,pickle.load(file)是pickle模块提供的方法之一,用于从文件对象file中读取序列化的数据,并将其反序列化为相应的Python对象。
hashlib和hmac模块 (hash哈希值)
hashlib模块
hash是一种算法(Python3.版本里使用hashlib模块代替了md5模块和sha模块,主要提供 SHA1、SHA224、SHA256、SHA384、SHA512、MD5 算法),该算法接受传入的内容,经过运算得到一串hash值。
hash值的特点:
只要传入的内容一样,得到的hash值一样,可用于非明文密码传输时密码校验
不能由hash值返解成内容,即可以保证非明文密码的安全性
只要使用的hash算法不变,无论校验的内容有多大,得到的hash值长度是固定的,可以用于对文本的哈希处理
hash算法其实可以看成如下图所示的一座工厂,工厂接收你送来的原材料,经过加工返回的产品就是hash值
import hashlib
import hashlib
m = hashlib.md5()
m.update('hello'.encode('utf8'))
print(m.hexdigest())
5d41402abc4b2a76b9719d911017c592
在Python中,hashlib.md5()是hashlib模块中提供的方法之一,用于创建MD5哈希对象。
m = hashlib.md5()创建了一个MD5哈希对象,并将其赋值给变量m。
然后,m.update('hello'.encode('utf8'))使用update()方法,将字符串’hello’按照UTF-8编码转换为字节流,并更新到哈希对象中。
最后,m.hexdigest()方法返回哈希值的十六进制表示。
撞库破解hash算法加密
hash加密算法虽然看起来很厉害,但是他是存在一定缺陷的,即可以通过撞库可以反解,如下代码所示。
import hashlib
pwd_list = [
'hash3714',
'hash1313',
'hash94139413',
'hash123456',
'123456hash',
'h123ash',
]
def make_pwd_dic(pwd_list):
dic = {}
for pwd in pwd_list:
m = hashlib.md5()
m.update(pwd.encode('utf-8'))
dic[pwd] = m.hexdigest()
return dic
def break_code(hash_pwd,pwd_dic):
for k,v in pwd_dic.items():
if v == hash_pwd:
print('hash的微信密码是===>%s' % k)
hash_pwd = '0562b36c3c5a3925dbe3c4d32a4f2ba2'
break_code(hash_pwd,make_pwd_dic(pwd_list))
hash的微信密码是===>hash123456
定义了一个函数make_pwd_dic(pwd_list)和一个函数break_code(hash_pwd, pwd_dic)。
make_pwd_dic(pwd_list)函数用于创建一个密码字典,将密码列表中的密码通过MD5哈希算法进行加密,并将加密结果与对应的原始密码构建成字典。
在函数内部,你使用循环遍历密码列表pwd_list,针对每个密码,创建一个MD5哈希对象m,然后使用m.update(pwd.encode('utf-8'))更新哈希对象的内容,最后将加密后的哈希值以键值对的形式添加到字典dic中。
break_code(hash_pwd, pwd_dic)函数用于尝试破解哈希密码。它接受一个哈希密码hash_pwd和密码字典pwd_dic作为参数。在函数内部,它会遍历密码字典pwd_dic,检查每个密码的哈希值是否与给定的哈希密码匹配。如果匹配成功,则打印对应的原始密码。
最后,你提供了一个示例,通过调用break_code(hash_pwd, make_pwd_dic(pwd_list))来尝试破解哈希密码hash_pwd。你的示例中,hash_pwd是一个已知的哈希密码,pwd_list是已知的密码列表。根据对密码列表进行哈希加密后的结果构建密码字典,并使用破解函数尝试破解哈希密码。
运行代码后,如果密码字典中存在与给定哈希密码匹配的密码,将会输出对应的原始密码。如果密码字典中不存在匹配的密码,则不会有输出。
为了防止密码被撞库,我们可以使用python中的另一个hmac 模块,它内部对我们创建key和内容做过某种处理后再加密。
如果要保证hmac模块最终结果一致,必须保证:
- hmac.new括号内指定的初始key一样
- 无论update多少次,校验的内容累加到一起是一样的内容
import hmac
h1 = hmac.new(b'hash')
h1.update(b'hello')
h1.update(b'world')
print(h1.hexdigest())
905f549c5722b5850d602862c34a763e
使用了hmac模块来计算HMAC(Hash-based Message Authentication Code),并打印输出结果。
在Python中,hmac模块提供了一种计算HMAC的方式,HMAC是一种用于消息认证的哈希算法。
在代码中,hmac.new(b'hash')创建了一个HMAC对象,传入的参数是密钥。这里的密钥是字节串b'hash'。
然后,使用h1.update(b'hello')和h1.update(b'world')方法分别更新了HMAC对象的内容。这里输入的数据需要是字节串。
最后,使用h1.hexdigest()方法获取HMAC值的十六进制表示。
HMAC算法在保证数据完整性和认证的过程中起到了重要的作用。它结合了哈希算法和密钥,通过对消息进行哈希计算,并使用密钥对计算结果进行加密,从而生成认证码。在验证消息时,同样的密钥和哈希算法会被用来重新计算认证码,并与接收到的认证码进行比较,以确定消息是否被篡改过。
请注意,使用HMAC时,密钥应当是保密的,并且必须采用适当的安全策略来保护密钥,以确保系统的安全性
h2 = hmac.new(b'hash')
h2.update(b'helloworld')
print(h2.hexdigest())
905f549c5722b5850d602862c34a763e
h3 = hmac.new(b'hashhelloworld')
print(h3.hexdigest())
a7e524ade8ac5f7f33f3a39a8f63fd25
logging模块 (日志处理模块)
低配logging
日志总共分为以下五个级别,这个五个级别自下而上进行匹配 debug-->info-->warning-->error-->critical,默认最低级别为warning级别。
v1
import logging
logging.debug('调试信息')
logging.info('正常信息')
logging.warning('警告信息')
logging.error('报错信息')
logging.critical('严重错误信息')
WARNING:root:警告信息
ERROR:root:报错信息
CRITICAL:root:严重错误信息
v1版本无法指定日志的级别;无法指定日志的格式;只能往屏幕打印,无法写入文件。因此可以改成下述的代码。
v2
import logging
# 日志的基本配置
logging.basicConfig(filename='access.log',
format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %P',
level=10)
logging.debug('调试信息')
logging.info('正常信息')
logging.warning('警告信息')
logging.error('报错信息')
logging.critical('严重错误信息')
可在logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为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用户输出的消息
v2版本不能指定字符编码;只能往文件中打印。
v3
logging模块包含四种角色:logger、Filter、Formatter对象、Handler
- logger:产生日志的对象
- Filter:过滤日志的对象
- Formatter对象:可以定制不同的日志格式对象,然后绑定给不同的Handler对象使用,以此来控制不同的Handler的日志格式
- Handler:接收日志然后控制打印到不同的地方,FileHandler用来打印到文件中,StreamHandler用来打印到终端
'''
critical=50
error =40
warning =30
info = 20
debug =10
'''
import logging
#1.logger对象:负责产生日志,然后交给Filter过滤,然后交给不同的Handler输出
logger = logging.getlogger(_file_)
#2.Filter对象:不常用,略
#3.Handler对象:接收logger传来的日志,然后控制输出
h1 = logging.FileHandler('t1.log')# 打印到文件
h2 = logging.FileHandler('t2.log')# 打印到文件
sm = logging.StreamHanler() # 打印到终端
#4.Formatter对象:日志格式
formmater1 = logging.Formatter('%(asctime)s' - %(name)s - %(levelname)s - %(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',)
formmater2 = logging.Formatter('%(asctime)s : %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',)
formmater3 = logging.Formatter('%(name)s %(message)s',)
# 5、为Handler对象绑定格式
h1.setFormatter(formmater1)
h2.setFormatter(formmater2)
sm.setFormatter(formmater3)
# 6、将Handler添加给logger并设置日志级别
logger.addHandler(h1)
logger.addHandler(h2)
logger.addHandler(sm)
# 设置日志级别,可以在两个关卡进行设置logger与handler
# logger是第一级过滤,然后才能到handler
logger.setLevel(30)
h1.setLevel(10)
h2.setLevel(10)
sm.setLevel(10)
# 7、测试
logger.debug('debug')
logger.info('info')
logger.warning('warning')
logger.error('error')
logger.critical('critical')
高配logging
配置日志文件
以上三个版本的日志只是为了引出我们下面的日志配置文件
import os
import logging.config
# 定义三种日志输出格式 开始
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' # 其中name为getLogger()指定的名字;lineno为调用日志输出函数的语句所在的代码行
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# 定义日志输出格式 结束
logfile_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # log文件的目录,需要自定义文件路径 # atm
logfile_dir = os.path.join(logfile_dir, 'log') # C:\Users\oldboy\Desktop\atm\log
logfile_name = 'log.log' # log文件名,需要自定义路径名
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir): # C:\Users\oldboy\Desktop\atm\log
os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name) # C:\Users\oldboy\Desktop\atm\log\log.log
# 定义日志路径 结束
# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {}, # filter可以不定义
'handlers': {
# 打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
# 打印到文件的日志,收集info及以上的日志
'default': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
'filename': logfile_path, # 日志文件
'maxBytes': 1024 * 1024 * 5, # 日志大小 5M (*****)
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
# logging.getLogger(__name__)拿到的logger配置。如果''设置为固定值logger1,则下次导入必须设置成logging.getLogger('logger1')
'': {
# 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'handlers': ['default', 'console'],
'level': 'DEBUG',
'propagate': False, # 向上(更高level的logger)传递
},
},
}
def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
logger = logging.getLogger(__name__) # 生成一个log实例
logger.info('It works!') # 记录该文件的运行状态
return logger
if __name__ == '__main__':
load_my_logging_cfg()
使用日志
import time
import logging
import my_logging # 导入自定义的logging配置
logger = logging.getLogger(__name__) # 生成logger实例
def demo():
logger.debug("start range... time:{}".format(time.time()))
logger.info("中文测试开始。。。")
for i in range(10):
logger.debug("i:{}".format(i))
time.sleep(0.2)
else:
logger.debug("over range... time:{}".format(time.time()))
logger.info("中文测试结束。。。")
if __name__ == "__main__":
my_logging.load_my_logging_cfg() # 在你程序文件的入口加载自定义logging配置
demo()
Django日志配置文件
# logging_config.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
'[%(levelname)s][%(message)s]'
},
'simple': {
'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
},
'collect': {
'format': '%(message)s'
}
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
# 打印到终端的日志
'console': {
'level': 'DEBUG',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
# 打印到文件的日志,收集info及以上的日志
'default': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切
'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日志文件
'maxBytes': 1024 * 1024 * 5, # 日志大小 5M
'backupCount': 3,
'formatter': 'standard',
'encoding': 'utf-8',
},
# 打印到文件的日志:收集错误及以上的日志
'error': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切
'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"), # 日志文件
'maxBytes': 1024 * 1024 * 5, # 日志大小 5M
'backupCount': 5,
'formatter': 'standard',
'encoding': 'utf-8',
},
# 打印到文件的日志
'collect': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切
'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"),
'maxBytes': 1024 * 1024 * 5, # 日志大小 5M
'backupCount': 5,
'formatter': 'collect',
'encoding': "utf-8"
}
},
'loggers': {
# logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console', 'error'],
'level': 'DEBUG',
'propagate': True,
},
# logging.getLogger('collect')拿到的logger配置
'collect': {
'handlers': ['console', 'collect'],
'level': 'INFO',
}
},
}
# -----------
# 用法:拿到俩个logger
logger = logging.getLogger(__name__) # 线上正常的日志
collect_logger = logging.getLogger("collect") # 领导说,需要为领导们单独定制领导们看的日志
numpy模块
numpy官方文档:https://docs.scipy.org/doc/numpy/reference/?v=20190307135750
numpy是Python的一种开源的数值计算扩展库。这种库可用来存储和处理大型numpy数组,比Python自身的嵌套列表结构要高效的多(该结构也可以用来表示numpy数组)。
numpy库有两个作用:
- 区别于list列表,提供了数组操作、数组运算、以及统计分布和简单的数学模型
- 计算速度快,甚至要由于python内置的简单运算,使得其成为pandas、sklearn等模块的依赖包。高级的框架如TensorFlow、PyTorch等,其数组操作也和numpy非常相似。
为什么要用numpy
lis1 = [1,2,3]
lis2 = [4,5,6]
lis1
[1, 2, 3]
lis2
[4, 5, 6]
如果我们想让lis1 * lis2得到一个结果为lis_res = [4, 10, 18],非常复杂。
创建numpy数组
numpy数组即numpy的ndarray对象,创建numpy数组就是把一个列表传入np.array()方法。
import numpy as np
# np.array? 相当于pycharm的ctrl+鼠标左键
# 创建一维的ndarray对象
arr = np.array([1,2,3])
print(arr,type(arr))
[1 2 3] <class 'numpy.ndarray'>
在numpy中,ndarray(N-dimensional array)是numpy提供的多维数组对象,用于存储和处理大量数据。
# 创建二维的ndarray对象+
print(np.array([[1,2,3],[4,5,6]]))
[[1 2 3]
[4 5 6]]
# 创建三维的ndarray对象
print(np.array([[1,2,3],[4,5,6],[7,8,9]]))
[[1 2 3]
[4 5 6]
[7 8 9]]
numpy数组的常用属性
属性 | 解释 |
---|---|
T | 数组的转置(对高维数组而言) |
dtype | 数组元素的数据类型 |
size | 数组元素的个数 |
ndim | 数组的维数 |
shape | 数组的维度大小(以元组形式) |
astype | 类型转换 |
dtype种类:bool_, int(8,16,32,64), float(16,32,64)
arr = np.array([[1,2,3],[4,5,6]],dtype=np.float32)
print(arr)
[[1. 2. 3.]
[4. 5. 6.]]
np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32)创建了一个包含两个子列表的二维数组。每个子列表是一个一维数组,分别包含1、2、3和4、5、6这几个元素。最后,通过dtype=np.float32指定了数组的数据类型为float32,也就是单精度浮点数
print(arr.T)
[[1. 4.]
[2. 5.]
[3. 6.]]
print(arr.T)将会输出原数组arr的转置数组。
print(arr.dtype)
float32
print(arr.dtype) 将输出数组 arr 的数据类型。
arr = arr.astype(np.int32)
print(arr.dtype)
print(arr)
int32
[[1 2 3]
[4 5 6]]
通过使用 arr.astype(np.int32),你将数组 arr 的数据类型转换为了 int32。
print(arr.size)
6
print(arr.size) 将输出数组 arr 的大小,即数组中元素的总数
print(arr.ndim)
2
print(arr.ndim) 将输出数组 arr 的维度数
print(arr.shape)
(2, 3)
print(arr.shape) 将输出数组 arr 的形状。
数组 arr 是一个包含两个子数组的二维数组。每个子数组都包含三个元素。因此,数组 arr 的形状为 (2, 3)。
获取numpy数组的行列数
由于numpy数组是多维的,对于二维的数组而言,numpy数组就是既有行又有列。
注意:对于numpy我们一般多讨论二维的数组。
import numpy as np
arr = np.array([[1,2,3],[4,5,6]])
print(arr)
[[1 2 3]
[4 5 6]]
# 获取numpy数组的行和列构成的数组
print(arr.shape)
(2, 3)
arr.shape会返回一个元组,该元组表示数组的形状。对于二维数组来说,形状的元组有两个值,第一个值代表行数,第二个值代表列数。
# 获取numpy数组的行
print(arr.shape[0])
2
arr.shape[0]表示获取数组形状元组的第一个元素,即数组的行数
# 获取numpy数组的列
print(arr.shape[1])
3
arr.shape[1]表示获取数组形状元组的第二个元素,即数组的列数
切割numpy数组
切分numpy数组类似于列表的切割,但是与列表的切割不同的是,numpy数组的切割涉及到行和列的切割,但是两者切割的方式都是从索引0开始,并且取头不取尾。
arr = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
print(arr)
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
arr[0, 0]的值是1,arr[0, 1]的值是2,以此类推
# 取所有元素
print(arr[: , :])
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
arr[:, :]表示获取数组arr中的所有元素。冒号 : 表示取该维度的所有值。
# 取第一行的所有元素
print(arr[:1,:])
[[1 2 3 4]]
# 取第一行的所有元素
print(arr[0,[0,1,2,3]])
[1 2 3 4]
arr[0, [0, 1, 2, 3]]表示获取数组arr中第一行的索引为0、1、2和3的元素。
# 取第一列的所有元素
print(arr[:,:1])
[[1]
[5]
[9]]
# 取第一列的所有元素
print(arr[(0,1,2),0])
[1 5 9]
arr[(0, 1, 2), 0]表示获取数组arr中索引为(0, 1, 2)的行和索引为0的列交叉处的元素。
# 取第一行第一列的元素
print(arr[0, 0])
1
# 取大于5的元素,返回一个数组
print(arr[arr > 5])
[ 6 7 8 9 10 11 12]
# numpy数组按运算符取元素的原理,即通过arr > 5生成一个布尔numpy数组
print(arr > 5)
[[False False False False]
[False True True True]
[ True True True True]]
通过 arr > 5 这个表达式可以生成一个布尔型的 numpy 数组(Boolean numpy array)。这个数组的形状和原始的 numpy 数组 arr 保持一致,每个元素对应的是对应位置的原始数组元素是否大于5的布尔值。
这个结果是一个形状相同的布尔型数组,其中 True 表示对应位置的元素大于5,False 表示对应位置的元素小于或等于5。
请注意,这个布尔型数组仅仅是用于标记 arr 中元素是否满足某种条件,如果你需要得到满足条件的元素本身,可以使用 arr[arr > 5] 这样的索引操作来获取新的数组。
numpy数组元素替换
numpy数组元素的替换,类似于列表元素的替换,并且numpy数组也是一个可变类型的数据,即如果对numpy数组进行替换操作,会修改原numpy数组的元素,所以下面我们用.copy()方法举例numpy数组元素的替换。
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print(arr)
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
# 取第一行的所有元素,并且让第一行的元素都为0
arr1 = arr.copy()
arr1[:1, :] = 0
print(arr1)
[[ 0 0 0 0]
[ 5 6 7 8]
[ 9 10 11 12]]
# 取所有大于5的元素,并且让大于5的元素为0
arr2 = arr.copy()
arr2[arr > 5] = 0
print(arr2)
[[1 2 3 4]
[5 0 0 0]
[0 0 0 0]]
# 对numpy数组清零
arr3 = arr.copy()
arr3[:, :] = 0
print(arr3)
[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]]
numpy数组的合并
arr1 = np.array([[1, 2], [3, 4], [5, 6]])
print(arr1)
[[1 2]
[3 4]
[5 6]]
arr2 = np.array([[7, 8], [9, 10], [11, 12]])
print(arr2)
[[ 7 8]
[ 9 10]
[11 12]]
# 合并两个numpy数组的行,注意使用hstack()方法合并numpy数组,numpy数组应该有相同的行,
# 其中hstack的h表示horizontal水平的
print(np.hstack((arr1, arr2)))
[[ 1 2 7 8]
[ 3 4 9 10]
[ 5 6 11 12]]
np.hstack() 函数将两个数组 arr1 和 arr2 水平地堆叠在一起。水平堆叠意味着将两个数组按列方向进行拼接,结果是一个新的数组。
通过水平堆叠,你可以将多个数组按列方向进行连接,从而创建一个包含更多列的新数组。
# 合并两个numpy数组,其中axis=1表示合并两个numpy数组的行
print(np.concatenate((arr1, arr2), axis=1))
[[ 1 2 7 8]
[ 3 4 9 10]
[ 5 6 11 12]]
# 合并两个numpy数组的列,注意使用vstack()方法合并numpy数组,numpy数组应该有相同的列,其中vstack的v表示vertical垂直的
print(np.vstack((arr1, arr2)))
[[ 1 2]
[ 3 4]
[ 5 6]
[ 7 8]
[ 9 10]
[11 12]]
使用了 np.vstack() 函数来将两个 numpy 数组 arr1 和 arr2 垂直地堆叠在一起。垂直堆叠意味着将两个数组按行方向进行拼接,结果是一个新的数组。
# 合并两个numpy数组,其中axis=0表示合并两个numpy数组的列
print(np.concatenate((arr1, arr2), axis=0))
[[ 1 2]
[ 3 4]
[ 5 6]
[ 7 8]
[ 9 10]
[11 12]]
使用 np.concatenate() 函数来合并两个 numpy 数组 arr1 和 arr2,并通过 axis=0 参数指定按列的方向进行合并。
通过函数创建numpy数组
方法 | 详解 |
---|---|
array() | 将列表转换为数组,可选择显式指定dtype |
arange() | range的numpy版,支持浮点数 |
linspace() | 类似arange(),第三个参数为数组长度 |
zeros() | 根据指定形状和dtype创建全0数组 |
ones() | 根据指定形状和dtype创建全1数组 |
eye() | 创建单位矩阵 |
empty() | 创建一个元素全随机的数组 |
reshape() | 重塑形状 |
array
import numpy as np
arr = np.array([1,2,3])
print(arr)
[1 2 3]
arange
# 构造0-9的ndarray数组
print(np.arange(10))
[0 1 2 3 4 5 6 7 8 9]
# 构造1-4的ndarray数组
print(np.arange(1, 5))
[1 2 3 4]
# 构造1-19且步长为2的ndarray数组
print(np.arange(1, 20, 2))
[ 1 3 5 7 9 11 13 15 17 19]
linspace/logspace
# 构造一个等差数列,取头也取尾,从0取到20,取5个数
print(np.linspace(0,20,5))
[ 0. 5. 10. 15. 20.]
# 构造一个等比数列,从10**0取到10**20,取5个数
print(np.logspace(0,20,5))
[1.e+00 1.e+05 1.e+10 1.e+15 1.e+20]
使用 np.logspace(0, 20, 5) 来构造一个 numpy 数组,它是从 10^0 取到 10^20 的等比数列,共取 5 个数。
np.logspace() 函数的参数指定的是数列的起始指数和终止指数(取 10 的幂),而不是直接指定数列的起始值和终止值。
zeros/ones/eye/empty
# 构造3*4的全0numpy数组 个元素都是浮点数类型。
print(np.zeros((3,4)))
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
# 构造3*4的全1numpy数组
print(np.ones((3,4)))
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
# 构造3个主元的单位numpy数组
print(np.eye(3))
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
# 构造一个4*4的随机numpy数组,里面的元素是随机生成的
print(np.empty((4,4)))
[[4.67296746e-307 1.69121096e-306 9.34601642e-307 8.90071136e-308]
[8.34441742e-308 1.78022342e-306 6.23058028e-307 9.79107872e-307]
[6.89807188e-307 7.56594375e-307 6.23060065e-307 1.78021527e-306]
[8.34454050e-308 1.11261027e-306 1.15706896e-306 1.33512173e-306]]
reshape
arr= np.ones([2,2],dtype=int)
print(arr.reshape(4,1))
[[1]
[1]
[1]
[1]]
重塑操作不会改变数组的元素值,只会改变数组的形状。
fromstring/fromfunction(了解)
# fromstring通过对字符串的字符编码所对应ASCII编码的位置,生成一个ndarray对象
s = 'abcdef'
#np.int8 表示一个字符的字节数为8
print(np.fromstring(s,dtype=np.int8))
[ 97 98 99 100 101 102]
e:\python3\lib\site-packages\ipykernel_launcher.py:6: DeprecationWarning: The binary mode of fromstring is deprecated, as it behaves surprisingly on unicode inputs. Use frombuffer instead
使用 np.fromstring(s, dtype=np.int8) 将字符串 s 转换为一个 numpy 数组。根据指定的数据类型 np.int8,每个字符将被解读为一个占用 8 位的整数。
通过执行 print(np.fromstring(s, dtype=np.int8)),会输出一个包含字符编码的 numpy 数组
其中的每个元素对应着字符串中字符的 ASCII 编码值。每个字符都被解析为一个 8 位的有符号整数 (np.int8)。
需要注意的是,np.fromstring() 在较新的 numpy 版本中已经过时,并建议使用 np.frombuffer() 函数来代替。所以,如果你使用的是较新的版本,建议使用 np.frombuffer(s.encode(), dtype=np.int8) 来执行类似的操作。
def func(i,j):
"""其中i为numpy数组的行,j为numpy数组的列"""
return i * j
# 使用函数对numpy数组元素的行和列的索引做处理,得到当前元素的值,索引从0开始,并构造一个3*4的numpy数组
print(np.fromfunction(func,(3,4)))
[[0. 0. 0. 0.]
[0. 1. 2. 3.]
[0. 2. 4. 6.]]
定义了一个名为 func 的函数,该函数接受两个参数 i 和 j,其中 i 表示 numpy 数组的行索引,j 表示 numpy 数组的列索引。函数的作用是将行索引和列索引相乘,返回当前元素的值。
通过执行 np.fromfunction(func, (3, 4)),你使用函数 func 对索引进行处理,并构造了一个 3x4 大小的 numpy 数组。
在这个数组中,第一行的所有元素都是 0,第二行的元素等于列索引(0, 1, 2, 3),第三行的元素等于列索引的两倍。
numpy数组运算
运算符 | 说明 |
---|---|
+ | 两个numpy数组对应元素相加 |
- | 两个numpy数组对应元素相减 |
* | 两个numpy数组对应元素相乘 |
/ | 两个numpy数组对应元素相除,如果都是整数则取商 |
% | 两个numpy数组对应元素相除后取余数 |
**n | 单个numpy数组每个元素都取n次方,如**2:每个元素都取平方 |
arr1 = np.array([[1,2],[3,4],[5,6]])
print(arr1)
[[1 2]
[3 4]
[5 6]]
arr2 = np.array([[7, 8], [9, 10], [11, 12]])
print(arr2)
[[ 7 8]
[ 9 10]
[11 12]]
print(arr1 + arr2)
[[ 8 10]
[12 14]
[16 18]]
print(arr1**2)
[[ 1 4]
[ 9 16]
[25 36]]
numpy数组运算函数
numpy数组函数 | 详解 |
---|---|
np.sin(arr) | 对numpy数组 arr 中每个元素取正弦,sin(x) |
np.cos(arr) | 对numpy数组 arr 中每个元素取余弦,cos(x) |
np.tan(arr) | 对numpy数组 arr 中每个元素取正切,tan(x) |
np.arcsin(arr) | 对numpy数组 arr 中每个元素取反正弦,arcsin(x) |
np.arccos(arr) | 对numpy数组 arr 中每个元素取反余弦,arccos(x) |
np.arctan(arr) | 对numpy数组 arr 中每个元素取反正切,arctan(x) |
np.exp(arr) | 对numpy数组 arr 中每个元素取指数函数,exp(x) |
np.sqrt(arr) | 对numpy数组 arr 中每个元素开根号,√x |
一元函数: abs, sqrt, exp, log, ceil, floor, rint, trunc, modf, isnan, isinf, cos, sin, tan
二元函数: add, substract, multiply, divide, power, mod, maximum, mininum
arr = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
print(arr)
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
# 对numpy数组的所有元素取正弦
print(np.sin(arr))
[[ 0.84147098 0.90929743 0.14112001 -0.7568025 ]
[-0.95892427 -0.2794155 0.6569866 0.98935825]
[ 0.41211849 -0.54402111 -0.99999021 -0.53657292]]
# 对numpy数组的所有元素开根号
print(np.sqrt(arr))
[[1. 1.41421356 1.73205081 2. ]
[2.23606798 2.44948974 2.64575131 2.82842712]
[3. 3.16227766 3.31662479 3.46410162]]
# 对numpy数组的所有元素取反正弦,如果元素不在定义域内,则会取nan值
print(np.arcsin(arr * 0.1))
[[0.10016742 0.20135792 0.30469265 0.41151685]
[0.52359878 0.64350111 0.7753975 0.92729522]
[1.11976951 1.57079633 nan nan]]
e:\python3\lib\site-packages\ipykernel_launcher.py:3: RuntimeWarning: invalid value encountered in arcsin
This is separate from the ipykernel package so we can avoid doing imports until
numpy数组函数 np.arcsin() 对numpy数组中的所有元素进行反正弦运算。注意,如果元素不在函数的定义域内,将会返回 NaN (Not a Number) 值。
执行 np.arcsin(arr * 0.1) 表示对 numpy 数组 arr 中的每个元素乘以 0.1,并对结果进行反正弦运算。结果将生成一个新的 numpy 数组
# 判断矩阵元素中是否含有np.nan值
print(np.isnan(arr))
[[False False False False]
[False False False False]
[False False False False]]
numpy的函数 np.isnan() 来判断矩阵(或者 numpy 数组)中的元素是否含有 np.nan 值。
执行 np.isnan(arr) 将返回一个布尔类型的numpy数组,其中元素的值为 True 表示对应位置的元素是 np.nan,而值为 False 表示对应位置的元素不是 np.nan。
numpy数组矩阵化
numpy数组的转置,相当于numpy数组的行和列互换。
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr)
[[1 2 3]
[4 5 6]]
print(arr.transpose())
[[1 4]
[2 5]
[3 6]]
numpy数组的 transpose() 方法来对数组进行转置操作。
print(arr.T)
[[1 4]
[2 5]
[3 6]]
numpy数组的 .T 属性来对数组进行转置操作。
arr.transpose() 和 arr.T 都用于对numpy数组进行转置操作,但它们有一些细微的区别。
- arr.transpose() 是一个方法,需要通过调用数组对象来使用,例如 arr.transpose()。
- arr.T 是一个属性,可以通过直接在数组对象上使用点符号来访问,例如 arr.T。
此外,它们还存在一些差异:
- 如果原始数组的维度是1维,即形状为 (n,),那么 arr.transpose() 并不会对数组进行转置操作,而是返回原始数组本身。
- arr.transpose() 方法可以接受表示轴的参数,例如 arr.transpose((1, 0)) 可以指定对数组进行特定维度的转置操作。
- arr.T 属性不接受参数,它直接返回数组的转置结果。
总结起来,对于大多数情况下的二维数组,arr.transpose() 和 arr.T 的效果是相同的,都能实现数组的转置操作。但在处理一维数组或者需要指定轴进行转置操作时,它们的行为可能会有所不同。
numpy数组的逆
numpy数组行和列相同时,numpy数组才可逆。
arr = np.array([[1, 2, 3], [4, 5, 6], [9, 8, 9]])
print(arr)
[[1 2 3]
[4 5 6]
[9 8 9]]
print(np.linalg.inv(arr))
[[ 0.5 -1. 0.5 ]
[-3. 3. -1. ]
[ 2.16666667 -1.66666667 0.5 ]]
numpy的 np.linalg.inv() 函数来计算矩阵的逆。
np.linalg.inv(arr) 将返回输入矩阵的逆矩阵(如果可逆)。逆矩阵是一个与原始矩阵相乘后得到单位矩阵的矩阵。逆矩阵只对方阵(即行数等于列数)成立,并且原始矩阵必须是可逆的才能计算出逆矩阵。
# 单位numpy数组的逆是单位numpy数组本身
arr = np.eye(3)
print(arr)
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
在numpy中,可以使用 np.eye(N) 函数创建一个大小为N的单位矩阵。
print(np.linalg.inv(arr))
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
numpy数组数学和统计方法
方法 | 详解 |
---|---|
sum | 求和 |
cumsum | 累加求和 |
mean | 求平均数 |
std | 求标准差 |
var | 求方差 |
min | 求最小值 |
max | 求最大值 |
argmin | 求最小值索引 |
argmax | 求最大值索引 |
sort | 排序 |
最大最小值
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr)
[[1 2 3]
[4 5 6]
[7 8 9]]
# 获取numpy数组所有元素中的最大值
print(arr.max())
9
# 获取numpy数组所有元素中的最小值
print(arr.min())
1
# 获取举着每一行的最大值
print(arr.max(axis=0))
[7 8 9]
# 获取numpy数组每一列的最大值
print(arr.max(axis=1))
[3 6 9]
axis=0 表示沿着纵轴方向进行操作,而 axis=1 表示沿着横轴方向进行操作
# 获取numpy数组最大元素的索引位置
print(arr.argmax(axis=1))
[2 2 2]
argmax() 是NumPy数组的一个方法,用于返回数组中最大元素的索引位置。通过指定 axis=1 参数,你告诉它沿着横轴(行)的方向进行操作。
平均值
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr)
[[1 2 3]
[4 5 6]
[7 8 9]]
# 获取numpy数组所有元素的平均值
print(arr.mean())
5.0
mean() 是NumPy数组的一个方法,用于计算数组的平均值。
# 获取numpy数组每一列的平均值
print(arr.mean(axis=0))
[4. 5. 6.]
# 获取numpy数组每一行的平均值
print(arr.mean(axis=1))
[2. 5. 8.]
方差
方差公式为
mean(|x−x.mean()|^2)
其中x为numpy数组。
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr)
[[1 2 3]
[4 5 6]
[7 8 9]]
# 获取numpy数组所有元素的方差
print(arr.var())
6.666666666666667
var() 是NumPy数组的一个方法,用于计算数组的方差。执行 arr.var() 将返回数组的方差。
# 获取numpy数组每一列的元素的方差
print(arr.var(axis=0))
[6. 6. 6.]
# 获取numpy数组每一行的元素的方差
print(arr.var(axis=1))
[0.66666667 0.66666667 0.66666667]
标准差
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr)
[[1 2 3]
[4 5 6]
[7 8 9]]
# 获取numpy数组所有元素的标准差
print(arr.std())
2.581988897471611
std() 是NumPy数组的一个方法,用于计算数组的标准差。
# 获取numpy数组每一列的标准差
print(arr.std(axis=0))
[2.44948974 2.44948974 2.44948974]
# 获取numpy数组每一行的标准差
print(arr.std(axis=1))
[0.81649658 0.81649658 0.81649658]
中位数
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr)
[[1 2 3]
[4 5 6]
[7 8 9]]
# 获取numpy数组所有元素的中位数
print(np.median(arr))
5.0
# 获取numpy数组所有元素的中位数
print(np.median(arr))
5.0
# 获取numpy数组每一列的中位数\
print(np.median(arr, axis=0))
[4. 5. 6.]
# 获取numpy数组每一行的中位数
print(np.median(arr, axis=1))
[2. 5. 8.]
numpy数组求和
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr)
[[1 2 3]
[4 5 6]
[7 8 9]]
# 对numpy数组的每一个元素求和
print(arr.sum())
45
# 对numpy数组的每一列求和
print(arr.sum(axis=0))
[12 15 18]
# 对numpy数组的每一行求和
print(arr.sum(axis=1))
[ 6 15 24]
累加和
arr = np.array([1, 2, 3, 4, 5])
print(arr)
[1 2 3 4 5]
# 第n个元素为前n-1个元素累加和
print(arr.cumsum())
[ 1 3 6 10 15]
cumsum函数会返回一个新的numpy数组,其中每个元素都是原数组中该位置前面所有元素的累加和。
numpy.random生成随机数
函数名称 | 函数功能 | 参数说明 |
---|---|---|
rand(d0, d1, ⋯, dn) | 产生均匀分布的随机数 | dn为第n维数据的维度 |
randn(d0, d1, ⋯, dn) | 产生标准正态分布随机数 | dn为第n维数据的维度 |
randint(low[, high, size, dtype]) | 产生随机整数 | low: 最小值;high: 最大值;size: 数据个数 |
random_sample([size]) | 在[0,1)内产生随机数 | size为随机数的shape,可以为元组或列表 |
choice(a[, size]) | 从arr中随机选择指定数据 | arr为1维数组;size为数组形状 |
uniform(low, high [, size]) | 给定形状产生随机数组 | low为最小值;high为最大值,size为数组形状 |
shuffle(a) | 与random.shuffle相同 | a为指定数组 |
# RandomState()方法会让数据值随机一次,之后都是相同的数据
rs = np.random.RandomState(1)
print(rs.rand(10))
[4.17022005e-01 7.20324493e-01 1.14374817e-04 3.02332573e-01
1.46755891e-01 9.23385948e-02 1.86260211e-01 3.45560727e-01
3.96767474e-01 5.38816734e-01]
通过使用np.random.RandomState(seed)方法,可以创建一个具有特定种子值的随机数生成器对象。这个生成器将生成一系列随机数,但在给定种子值的情况下,每次运行程序时产生的随机数序列将保持一致。
np.random.RandomState(1)创建了一个具有种子值1的随机数生成器对象rs。然后,rs.rand(10)调用了该生成器的rand方法来生成一个包含10个随机数的numpy数组。
# 构造3*4的均匀分布的numpy数组
# seed()方法会让数据值随机一次,之后都是相同的数据
np.random.seed(1)
print(np.random.rand(3,4))
[[4.17022005e-01 7.20324493e-01 1.14374817e-04 3.02332573e-01]
[1.46755891e-01 9.23385948e-02 1.86260211e-01 3.45560727e-01]
[3.96767474e-01 5.38816734e-01 4.19194514e-01 6.85219500e-01]]
通过np.random.seed(1)设置了随机数生成器的种子值为1。这将确保每次运行程序时生成的随机数序列是相同的。
然后使用np.random.rand(3, 4)方法生成一个形状为3x4的均匀分布的numpy数组。np.random.rand函数生成一个指定形状的数组,并以0到1之间的均匀分布来填充数组的元素。
# 构造3*4*5的均匀分布的numpy数组
print(np.random.rand(3, 4, 5))
[[[0.20445225 0.87811744 0.02738759 0.67046751 0.4173048 ]
[0.55868983 0.14038694 0.19810149 0.80074457 0.96826158]
[0.31342418 0.69232262 0.87638915 0.89460666 0.08504421]
[0.03905478 0.16983042 0.8781425 0.09834683 0.42110763]]
[[0.95788953 0.53316528 0.69187711 0.31551563 0.68650093]
[0.83462567 0.01828828 0.75014431 0.98886109 0.74816565]
[0.28044399 0.78927933 0.10322601 0.44789353 0.9085955 ]
[0.29361415 0.28777534 0.13002857 0.01936696 0.67883553]]
[[0.21162812 0.26554666 0.49157316 0.05336255 0.57411761]
[0.14672857 0.58930554 0.69975836 0.10233443 0.41405599]
[0.69440016 0.41417927 0.04995346 0.53589641 0.66379465]
[0.51488911 0.94459476 0.58655504 0.90340192 0.1374747 ]]]
# 构造3*4的正态分布的numpy数组
print(np.random.randn(3, 4))
[[ 0.30017032 -0.35224985 -1.1425182 -0.34934272]
[-0.20889423 0.58662319 0.83898341 0.93110208]
[ 0.28558733 0.88514116 -0.75439794 1.25286816]]
# 构造取值为1-5内的10个元素的ndarray数组
print(np.random.randint(1, 5, 10))
[1 1 1 2 3 1 2 1 3 4]
# 构造取值为0-1内的3*4的numpy数组
print(np.random.random_sample((3, 4)))
[[0.5270581 0.8859421 0.35726976 0.90853515]
[0.62336012 0.01582124 0.92943723 0.69089692]
[0.99732285 0.17234051 0.13713575 0.93259546]]
arr = np.array([1, 2, 3])
# 随机选取arr中的两个元素
print(np.random.choice(arr, size=2))
[3 2]
arr = np.random.uniform(1, 5, (2, 3))
print(arr)
[[3.03299015 1.83427325 1.19721698]
[3.0754475 1.68911341 2.58525315]]
np.random.shuffle(arr)
print(arr)
[[3.03299015 1.83427325 1.19721698]
[3.0754475 1.68911341 2.58525315]]
np.random.shuffle(arr)对数组arr进行了就地shuffle操作。np.random.shuffle函数将原地重新排列数组的顺序,以创建一个随机的排列。
pandas模块 (处理文本或者表格数据)
pandas基于Numpy,可以看成是处理文本或者表格数据。pandas中有两个主要的数据结构,其中Series数据结构类似于Numpy中的一维数组,DataFrame类似于多维表格数据结构。
pandas是python数据分析的核心模块。它主要提供了五大功能:
- 支持文件存取操作,支持数据库(sql)、html、json、pickle、csv(txt、excel)、sas、stata、hdf等。
- 支持增删改查、切片、高阶函数、分组聚合等单表操作,以及和dict、list的互相转换。
- 支持多表拼接合并操作。
- 支持简单的绘图操作。
- 支持简单的统计分析操作。
Series数据结构
Series是一种类似于一维数组的对象,由一组数据和一组与之相关的数据标签(索引)组成。
Series比较像列表(数组)和字典的结合体
import numpy as np
import pandas as pd
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
<ipython-input-4-59ab05e21164> in <module>
1 import numpy as np
----> 2 import pandas as pd
ModuleNotFoundError: No module named 'pandas'
matplotlib模块
matplotlib官方文档:https://matplotlib.org/contents.html?v=20190307135750
matplotlib是一个绘图库,它可以创建常用的统计图,包括条形图、箱型图、折线图、散点图、饼图和直方图。
条形图
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
%matplotlib inline
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
# 修改背景为条纹
plt.style.use('ggplot')
classes = ['3班', '4班', '5班', '6班']
classes_index = range(len(classes))
print(list(classes_index))
[0, 1, 2, 3]
student_amounts = [66, 55, 45, 70]
# 画布设置
fig = plt.figure()
# 1,1,1表示一张画布切割成1行1列共一张图的第1个;2,2,1表示一张画布切割成2行2列共4张图的第一个(左上角)
ax1 = fig.add_subplot(1, 1, 1)
ax1.bar(classes_index, student_amounts, align='center', color='darkblue')
ax1.xaxis.set_ticks_position('bottom')
ax1.yaxis.set_ticks_position('left')
plt.xticks(classes_index,
classes,
rotation=0,
fontsize=13,
fontproperties=font)
plt.xlabel('班级', fontproperties=font, fontsize=15)
plt.ylabel('学生人数', fontproperties=font, fontsize=15)
plt.title('班级-学生人数', fontproperties=font, fontsize=20)
# 保存图片,bbox_inches='tight'去掉图形四周的空白
# plt.savefig('classes_students.png?x-oss-process=style/watermark', dpi=400, bbox_inches='tight')
plt.show()
直方图
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
%matplotlib inline
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
# 修改背景为条纹
plt.style.use('ggplot')
mu1, mu2, sigma = 50, 100, 10
# 构造均值为50的符合正态分布的数据
x1 = mu1 + sigma * np.random.randn(10000)
print(x1)
[44.67046517 36.80319224 47.25100455 ... 39.45901464 51.51075389
40.84983772]
# 构造均值为100的符合正态分布的数据
x2 = mu2 + sigma * np.random.randn(10000)
print(x2)
[111.35798425 93.27585772 108.89133623 ... 99.2856733 67.43995939
106.25477784]
fig = plt.figure()
ax1 = fig.add_subplot(121)
# bins=50表示每个变量的值分成50份,即会有50根柱子
ax1.hist(x1, bins=50, color='darkgreen')
ax2 = fig.add_subplot(122)
ax2.hist(x2, bins=50, color='orange')
fig.suptitle('两个正态分布', fontproperties=font, fontweight='bold', fontsize=15)
ax1.set_title('绿色的正态分布', fontproperties=font)
ax2.set_title('橙色的正态分布', fontproperties=font)
plt.show()
折线图
import numpy as np
from numpy.random import randn
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
%matplotlib inline
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
# 修改背景为条纹
plt.style.use('ggplot')
np.random.seed(1)
# 使用numpy的累加和,保证数据取值范围不会在(0,1)内波动
plot_data1 = randn(40).cumsum()
print(plot_data1)
[ 1.62434536 1.01258895 0.4844172 -0.58855142 0.2768562 -2.02468249
-0.27987073 -1.04107763 -0.72203853 -0.97140891 0.49069903 -1.56944168
-1.89185888 -2.27591324 -1.1421438 -2.24203506 -2.41446327 -3.29232169
-3.25010794 -2.66729273 -3.76791191 -2.6231882 -1.72159748 -1.21910314
-0.31824719 -1.00197505 -1.12486527 -2.06063471 -2.32852279 -1.79816732
-2.48982807 -2.8865816 -3.5737543 -4.41895994 -5.09020607 -5.10287067
-6.22018102 -5.98576532 -4.32596314 -3.58391898]
plot_data2 = randn(40).cumsum()
plot_data3 = randn(40).cumsum()
plot_data4 = randn(40).cumsum()
plt.plot(plot_data1, marker='o', color='red', linestyle='-', label='红实线')
plt.plot(plot_data2, marker='x', color='orange', linestyle='--', label='橙虚线')
plt.plot(plot_data3, marker='*', color='yellow', linestyle='-.', label='黄点线')
plt.plot(plot_data4, marker='s', color='green', linestyle=':', label='绿点图')
# loc='best'给label自动选择最好的位置
plt.legend(loc='best', prop=font)
plt.show()
散点图+直线图
import numpy as np
from numpy.random import randn
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
%matplotlib inline
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
# 修改背景为条纹
plt.style.use('ggplot')
x = np.arange(1, 20, 1)
print(x)
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
# 拟合一条水平散点线
np.random.seed(1)
y_linear = x + 10 * np.random.randn(19)
print(y_linear)
[ 17.24345364 -4.11756414 -2.28171752 -6.72968622 13.65407629
-17.01538697 24.44811764 0.38793099 12.19039096 7.50629625
25.62107937 -8.60140709 9.77582796 10.15945645 26.33769442
5.00108733 15.27571792 9.22141582 19.42213747]
# 拟合一条x²的散点线
y_quad = x**2 + 10 * np.random.randn(19)
print(y_quad)
[ 6.82815214 -7.00619177 20.4472371 25.01590721 30.02494339
45.00855949 42.16272141 62.77109774 71.64230566 97.3211192
126.30355467 137.08339248 165.03246473 189.128273 216.54794359
249.28753869 288.87335401 312.82689651 363.34415698]
# s是散点大小
fig = plt.figure()
ax1 = fig.add_subplot(121)
plt.scatter(x, y_linear, s=30, color='r', label='蓝点')
plt.scatter(x, y_quad, s=100, color='b', label='红点')
ax2 = fig.add_subplot(122)
plt.plot(x, y_linear, color='r')
plt.plot(x, y_quad, color='b')
# 限制x轴和y轴的范围取值
plt.xlim(min(x) - 1, max(x) + 1)
plt.ylim(min(y_quad) - 10, max(y_quad) + 10)
fig.suptitle('散点图+直线图', fontproperties=font, fontsize=20)
ax1.set_title('散点图', fontproperties=font)
ax1.legend(prop=font)
ax2.set_title('直线图', fontproperties=font)
plt.show()
饼图
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']
fig, ax = plt.subplots(subplot_kw=dict(aspect="equal"))
recipe = ['优', '良', '轻度污染', '中度污染', '重度污染', '严重污染', '缺']
data = [2, 49, 21, 9, 11, 6, 2]
colors = ['lime', 'yellow', 'darkorange', 'red', 'purple', 'maroon', 'grey']
wedges, texts, texts2 = ax.pie(data,
wedgeprops=dict(width=0.5),
startangle=40,
colors=colors,
autopct='%1.0f%%',
pctdistance=0.8)
plt.setp(texts2, size=14, weight="bold")
bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
kw = dict(xycoords='data',
textcoords='data',
arrowprops=dict(arrowstyle="->"),
bbox=None,
zorder=0,
va="center")
for i, p in enumerate(wedges):
ang = (p.theta2 - p.theta1) / 2. + p.theta1
y = np.sin(np.deg2rad(ang))
x = np.cos(np.deg2rad(ang))
horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
connectionstyle = "angle,angleA=0,angleB={}".format(ang)
kw["arrowprops"].update({"connectionstyle": connectionstyle})
ax.annotate(recipe[i],
xy=(x, y),
xytext=(1.25 * np.sign(x), 1.3 * y),
size=16,
horizontalalignment=horizontalalignment,
fontproperties=font,
**kw)
ax.set_title("饼图示例",fontproperties=font)
plt.show()
# plt.savefig('jiaopie2.png?x-oss-process=style/watermark')
箱型图
箱型图:又称为盒须图、盒式图、盒状图或箱线图,是一种用作显示一组数据分散情况资料的统计图(在数据分析中常用在异常值检测)
包含一组数据的:最大值、最小值、中位数、上四分位数(Q3)、下四分位数(Q1)、异常值
- 中位数 → 一组数据平均分成两份,中间的数
- 上四分位数Q1 → 是将序列平均分成四份,计算(n+1)/4与(n-1)/4两种,一般使用(n+1)/4
- 下四分位数Q3 → 是将序列平均分成四份,计算(1+n)/4*3=6.75
- 内限 → T形的盒须就是内限,最大值区间Q3+1.5IQR,最小值区间Q1-1.5IQR (IQR=Q3-Q1)
- 外限 → T形的盒须就是内限,最大值区间Q3+3IQR,最小值区间Q1-3IQR (IQR=Q3-Q1)
- 异常值 → 内限之外 - 中度异常,外限之外 - 极度异常
import numpy as np
import pandas as pd
from numpy.random import randn
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
%matplotlib inline
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
df = pd.DataFrame(np.random.rand(10, 5), columns=['A', 'B', 'C', 'D', 'E'])
plt.figure(figsize=(10, 4))
# 创建图表、数据
f = df.boxplot(
sym='o', # 异常点形状,参考marker
vert=True, # 是否垂直
whis=1.5, # IQR,默认1.5,也可以设置区间比如[5,95],代表强制上下边缘为数据95%和5%位置
patch_artist=True, # 上下四分位框内是否填充,True为填充
meanline=False,
showmeans=True, # 是否有均值线及其形状
showbox=True, # 是否显示箱线
showcaps=True, # 是否显示边缘线
showfliers=True, # 是否显示异常值
notch=False, # 中间箱体是否缺口
return_type='dict' # 返回类型为字典
)
plt.title('boxplot')
for box in f['boxes']:
box.set(color='b', linewidth=1) # 箱体边框颜色
box.set(facecolor='b', alpha=0.5) # 箱体内部填充颜色
for whisker in f['whiskers']:
whisker.set(color='k', linewidth=0.5, linestyle='-')
for cap in f['caps']:
cap.set(color='gray', linewidth=2)
for median in f['medians']:
median.set(color='DarkBlue', linewidth=2)
for flier in f['fliers']:
flier.set(marker='o', color='y', alpha=0.5)
# boxes, 箱线
# medians, 中位值的横线,
# whiskers, 从box到error bar之间的竖线.
# fliers, 异常值
# caps, error bar横线
# means, 均值的横线
plot函数参数
- 线型linestyle(-,-.,--,..)
- 点型marker(v,^,s,*,H,+,x,D,o,…)
- 颜色color(b,g,r,y,k,w,…)
图像标注参数
- 设置图像标题:plt.title()
- 设置x轴名称:plt.xlabel()
- 设置y轴名称:plt.ylabel()
- 设置X轴范围:plt.xlim()
- 设置Y轴范围:plt.ylim()
- 设置X轴刻度:plt.xticks()
- 设置Y轴刻度:plt.yticks()
- 设置曲线图例:plt.legend()
Matplolib应用
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
%matplotlib inline
# 找到自己电脑的字体路径,然后修改字体路径
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
header_list = ['方程组', '函数', '导数', '微积分', '线性代数', '概率论', '统计学']
py3_df = pd.read_excel('py3.xlsx', header=None,
skiprows=[0, 1], names=header_list)
# 处理带有NaN的行
py3_df = py3_df.dropna(axis=0)
print(py3_df)
# 自定义映射
map_dict = {
'不会': 0,
'了解': 1,
'熟悉': 2,
'使用过': 3,
}
for header in header_list:
py3_df[header] = py3_df[header].map(map_dict)
unable_series = (py3_df == 0).sum(axis=0)
know_series = (py3_df == 1).sum(axis=0)
familiar_series = (py3_df == 2).sum(axis=0)
use_series = (py3_df == 3).sum(axis=0)
unable_label = '不会'
know_label = '了解'
familiar_label = '熟悉'
use_label = '使用过'
for i in range(len(header_list)):
bottom = 0
# 描绘不会的条形图
plt.bar(x=header_list[i], height=unable_series[i],
width=0.60, color='r', label=unable_label)
if unable_series[i] != 0:
plt.text(header_list[i], bottom, s=unable_series[i],
ha='center', va='bottom', fontsize=15, color='white')
bottom += unable_series[i]
# 描绘了解的条形图
plt.bar(x=header_list[i], height=know_series[i],
width=0.60, color='y', bottom=bottom, label=know_label)
if know_series[i] != 0:
plt.text(header_list[i], bottom, s=know_series[i],
ha='center', va='bottom', fontsize=15, color='white')
bottom += know_series[i]
# 描绘熟悉的条形图
plt.bar(x=header_list[i], height=familiar_series[i],
width=0.60, color='g', bottom=bottom, label=familiar_label)
if familiar_series[i] != 0:
plt.text(header_list[i], bottom, s=familiar_series[i],
ha='center', va='bottom', fontsize=15, color='white')
bottom += familiar_series[i]
# 描绘使用过的条形图
plt.bar(x=header_list[i], height=use_series[i],
width=0.60, color='b', bottom=bottom, label=use_label)
if use_series[i] != 0:
plt.text(header_list[i], bottom, s=use_series[i],
ha='center', va='bottom', fontsize=15, color='white')
unable_label = know_label = familiar_label = use_label = ''
plt.xticks(header_list, fontproperties=font)
plt.ylabel('人数', fontproperties=font)
plt.title('Python3期数学摸底可视化', fontproperties=font)
plt.legend(prop=font, loc='upper left')
plt.show()
re模块(正则表达)
正则表达式
正则表达式本身是一种小型的、高度专业化的编程语言,它并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。如果已经在其他语言里使用过正则表达式,只需要简单看一看就可以上手了。而在python中,通过内嵌集成re模块,程序员们可以直接调用来实现正则匹配。正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行。
下图展示了使用正则表达式进行匹配的流程:
正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的,看下图中的示例以及自己多使用几次就能明白。
下图列出了Python支持的正则表达式元字符和语法:
数量词的贪婪模式与非贪婪模式
正则表达式通常用于在文本中查找匹配的字符串。Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则相反,总是尝试匹配尽可能少的字符。例如:正则表达式"ab"如果用于查找"abbbc",将找到"abbb"。而如果使用非贪婪的数量词"ab?",将找到"a"。
反斜杠的困扰
与大多数编程语言相同,正则表达式里使用\作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符\,那么使用编程语言表示的正则表达式里将需要4个反斜杠\\:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r'\'表示。同样,匹配一个数字的\d可以写成r'\d'。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
匹配模式
正则表达式提供了一些可用的匹配模式,比如忽略大小写、多行匹配等,这部分内容将在Pattern类的工厂方法re.compile(pattern[, flags])中一起介绍。
re模块的基本使用
正则表达式是用来匹配处理字符串的 python 中使用正则表达式需要引入re模块
import re
a = re.findall("匹配规则","这个字符串是否有匹配规则的字符")
print(a)
['匹配规则']
re.findall()函数来进行正则匹配。该函数的第一个参数是要匹配的规则,第二个参数是要进行匹配的字符串。
我们假设要匹配的规则是"匹配规则",要进行匹配的字符串是"这个字符串是否有匹配规则的字符"。
re.findall()函数会返回一个列表,其中包含所有匹配到的字符串。
^元字符
字符串开始位置与匹配规则符合就匹配,否则不匹配
匹配字符串开头。在多行模式中匹配每一行的开头(Python3+已经失效,配合compile使用)
^元字符如果写到[]字符集里就是反取
import re
a = re.findall("^匹配规则","匹配规则这个字符串的开头") #只看字符串的开始位置是否匹配
print(a)
['匹配规则']
[^a-z]反取
匹配出除字母外的字符,^元字符如果写到字符集里就是反取
import re
a = re.findall("[^a-z]","匹配s规则这个s字符串是否s匹配f规则则re则则则") # 反取,匹配出除字母外的字符
print(a)
['匹', '配', '规', '则', '这', '个', '字', '符', '串', '是', '否', '匹', '配', '规', '则', '则', '则', '则', '则']
$元字符
字符串结束位置与匹配规则符合就匹配,否则不匹配
匹配字符串末尾,在多行模式中匹配每一行的末尾
import re
a = re.findall("匹配规则$","这个字符串是否匹配规则")
print(a)
['匹配规则']
*元字符
需要字符串里完全符合,匹配规则,就匹配,(规则里的*元字符)前面的一个字符可以是0个或多个原本字符
匹配前一个字符0或多次,贪婪匹配前导字符有多少个就匹配多少个很贪婪
如果规则里只有一个分组,尽量避免用*否则会有可能匹配出空字符串
import re
# 需要字符串里完全符合,匹配规则,就匹配,(规则里的*元字符)前面的一个字符可以是0或多个原本字符
a = re.findall("匹配规则*","这个祖传是否匹配匹配匹匹匹匹配规则则则则则")
print(a)
['匹配规则则则则则']
+元字符
需要字符串里完全符合,匹配规则,就匹配,(规则里的+元字符)前面的一个字符可以是1个或多个原本字符
匹配前一个字符1次或无限次,贪婪匹配前导字符有多少个就匹配多少个很贪婪
import re
a = re.findall("匹配+","匹配配配配配规则这个字符串是否匹配匹配匹配匹配规则则则则则")
print(a)
['匹配配配配配', '匹配', '匹配', '匹配', '匹配']
?元字符(防止贪婪匹配)
需要字符串里完全符合,匹配规则,就匹配,(规则里的?元字符)前面的一个字符可以是0个或1个原本字符
匹配一个字符0次或1次
还有一个功能是可以防止贪婪匹配,详情见防贪婪匹配
import re
a = re.findall("匹配规则?", "匹配规这个字符串是否匹配规则则则则则")
print(a)
['匹配规', '匹配规则']
{}元字符(范围)
需要字符串里完全符合,匹配规则,就匹配,(规则里的 {} 元字符)前面的一个字符,是自定义字符数,位数的原本字符
{m}匹配前一个字符m次,{m,n}匹配前一个字符m至n次,若省略n,则匹配m至无限次
{0,}匹配前一个字符0或多次,等同于*元字符
{+,}匹配前一个字符1次或无限次,等同于+元字符
{0,1}匹配前一个字符0次或1次,等同于?元字符
import re
a = re.findall("匹配规则{3}", "匹配规这个字符串是否匹配规则则则则则")
print(a)
['匹配规则则则']
[]元字符(字符集)
需要字符串里完全符合,匹配规则,就匹配,(规则里的 [] 元字符)对应位置是[]里的任意一个字符就匹配
字符集。对应的位置可以是字符集中任意字符。字符集中的字符可以逐个列出,也可以给出范围,如[abc]或[a-c]。[^abc]表示取反,即非abc。
所有特殊字符在字符集中都失去其原有的特殊含义。用\反斜杠转义恢复特殊字符的特殊含义。
import re
# 需要字符串里完全符合,匹配规则,就匹配,(规则里的 [] 元字符)对应位置是[]里的任意一个字符就匹配
a = re.findall("匹配[a,b,c]规则", "匹配a规则这个字符串是否匹配b规则则则则则")
print(a)
['匹配a规则', '匹配b规则']
[^]
非,反取,匹配出除[]里面的字符,元字符如果写到字符集里就是反取
import re
a = re.findall("[^a-z]", "匹配s规则这s个字符串是否s匹配f规则则re则则则") # 反取,匹配出除字母外的字符
print(a)
['匹', '配', '规', '则', '这', '个', '字', '符', '串', '是', '否', '匹', '配', '规', '则', '则', '则', '则', '则']
反斜杠后边跟普通字符实现特殊功能(预定义字符)
预定义字符是在字符集和组里都是有用的
\d匹配任何十进制数,它相当于类[0-9]
import re
a = re.findall("\d", "匹配规则这2个字符串3是否匹配规则5则则则7则") # \d匹配任何十进制数,它相当于类[0-9]
print(a)
['2', '3', '5', '7']
\d+
匹配一位或者多位数的数字时用
import re
a = re.findall("\d+", "匹配规则这2个字符串134444是否匹配规则5则则则7则") # \d+如果需要匹配一位或者多位数的数字时用
print(a)
['2', '134444', '5', '7']
\D
匹配任何非数字字符,它相当于类[^0-9]
import re
a = re.findall("\D", "匹配规则这2个字符串3是否匹配规则5则则则7则") # \D匹配任何非数字字符,它相当于类[^0-9]
print(a)
['匹', '配', '规', '则', '这', '个', '字', '符', '串', '是', '否', '匹', '配', '规', '则', '则', '则', '则', '则']
\s
匹配任何空白字符,它相当于类[\t\n\r\f\v]
import re
# \s匹配任何空白字符,它相当于类[\t\n\r\f\v]
a = re.findall("\s", "匹配规则 这2个字符串3是否匹\n配规则5则则则7则")
print(a)
[' ', ' ', ' ', '\n']
\S
匹配任何非空白字符,它相当于类[^\t\n\r\f\v]
import re
# \S匹配任何非空白字符,它相当于类[^\t\n\r\f\v]
a = re.findall("\S", "匹配规则 这2个字符串3是否匹\n配规则5则则则7则")
print(a)
['匹', '配', '规', '则', '这', '2', '个', '字', '符', '串', '3', '是', '否', '匹', '配', '规', '则', '5', '则', '则', '则', '7', '则']
\w
匹配包括下划线在内任何字母数字字符,它相当于类[a-zA-Z0-9_]
import re
# \w匹配包括下划线在内任何字母数字字符,它相当于类[a-zA-Z0-9_]
a = re.findall('\w', "https://www.cnblogs.com/")
print(a)
['h', 't', 't', 'p', 's', 'w', 'w', 'w', 'c', 'n', 'b', 'l', 'o', 'g', 's', 'c', 'o', 'm']
\W
匹配非任何字母数字字符包括下划线在内,它相当于类[^a-zA-Z0-9_]
import re
# \w匹配包括下划线在内任何字母数字字符,它相当于类[a-zA-Z0-9_]
a = re.findall('\W', "https://www.cnblogs.com/")
print(a)
[':', '/', '/', '.', '.', '/']
()元字符(分组)
也就是分组匹配,()里面的为一个组也可以理解成一个整体
如果()后面跟的是特殊元字符如 (adc)* 那么*控制的前导字符就是()里的整体内容,不再是前导一个字符
import re
# 也就是分组匹配,()里面的为一个组也可以理解成一个整体
a = re.search("(a4)+", "a4a4a4a4a4dg4g654gb") # 匹配一个或多个a4
b = a.group()
print(b)
a4a4a4a4a4
re.search()函数进行正则匹配。该函数的第一个参数是要匹配的规则,第二个参数是要进行匹配的字符串。
在这个例子中,我们的匹配规则是"(a4)+“,其中”(a4)“表示匹配字符"a4”,“+“表示匹配前面的整个组(”(a4)”)一个或多个。
被匹配的字符串是"a4a4a4a4a4dg4g654gb",我们的规则将匹配连续出现的"a4"。
re.search()函数会返回第一个符合匹配规则的匹配对象。
使用.group()方法获取匹配对象的匹配结果,将其赋值给变量b。
import re
# 也就是分组匹配,()里面的为一个组也可以理解成一个整体
# 匹配 (a) (\d0-9的数字) (+可以是1个到多个0-9的数字)
a = re.search("a(\d+)", "a466666664a4a4a4dg4g654gb")
b = a.group()
print(b)
a466666664
|元字符(或)
|或,或就是前后其中一个符合就匹配
import re
a = re.findall(r"你|好", "a4a4a你4aabc4a4dgg好dg4g654g") # |或,或就是前后其中一个符合就匹配
print(a)
['你', '好']
re模块中常用功能函数
正则表达式的两种书写方式
- 直接在函数里面抒写规则 使用较多
import re
a = re.findall("匹配规则", "这个字符串是否有匹配规则的字符")
print(a)
['匹配规则']
- 另一种是先将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得匹配结果(一个Match实例),最后使用Match实例获得信息,进行其他的操作。
import re
# 将正则表达式编译成Pattern对象
pattern = re.compile(r'hello')
# 使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None
match = pattern.match('hello world')
if match:
# 使用Match获得分组信息
print(match.group())
hello
使用re.compile()函数将正则表达式编译为Pattern对象。在这个例子中,我们将正则表达式设为r'hello'。
使用re.compile()函数可以提前编译正则表达式,以便重复使用,能够提高匹配的效率。
然后,我们使用Pattern对象的match()方法进行匹配。该方法的第一个参数是要进行匹配的文本,这里是'hello world!'。
如果匹配成功,match变量将不为None,然后我们可以使用match.group()方法获取匹配到的结果。
最后,使用print()函数打印匹配到的结果。
re.compile(strPattern[, flag])函数
这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。 第二个参数flag是匹配模式,取值可以使用按位或运算符'|'表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)与re.compile('(?im)pattern')是等价的。
下表是所有的正则匹配模式:
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符 |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解 |
re.S
在Python的正则表达式中,有一个参数为re.S。它表示 “.” 的作用扩展到整个字符串,包括“\n”。看如下代码:
import re
a = '''asdfhellopass:
worldaf
'''
b = re.findall('hello(.*?)world', a)
c = re.findall('hello(.*?)world', a, re.S)
print('b is ', b)
print('c is ', c)
b is []
c is ['pass:\n ']
定义了一个多行字符串a,其中包含了一个匹配规则需要匹配的内容。这个多行字符串使用三引号(‘’’ ‘’')包围,可以跨行定义字符串。
然后,我们使用re.findall()函数进行正则匹配。该函数的第一个参数是要匹配的规则,第二个参数是要进行匹配的字符串。在这个例子中,我们要匹配的规则是'hello(.*?)world'。
'hello'和'world'是两个固定的字符串,(.*?)表示匹配中间的任意字符(非贪婪匹配),re.S是一个标志参数,表示将匹配规则应用于包括换行符在内的所有字符。
re.findall()函数将返回一个列表,其中包含所有匹配到的字符串。
我们使用两个不同的调用方式进行匹配,一个是不带re.S标志参数,另一个是带有re.S标志参数。
最后,使用print()函数打印变量b和c的值。
在这个例子中,匹配结果如下:
不带re.S标志参数的匹配结果是[],因为默认情况下'.'并不匹配换行符,所以无法匹配到多行的内容。
带有re.S标志参数的匹配结果是['pass:\n '],这是因为re.S标志参数使得'.'匹配到了换行符,从'hello'到'world'之间的内容被匹配到了。
正则表达式中,“.”的作用是匹配除“\n”以外的任何字符,也就是说,它是在一行中进行匹配。这里的“行”是以“\n”进行区分的。a字符串有每行的末尾有一个“\n”,不过它不可见。
如果不使用re.S参数,则只在每一行内进行匹配,如果一行没有,就换下一行重新开始,不会跨行。而使用re.S参数以后,正则表达式会将这个字符串作为一个整体,将“\n”当做一个普通的字符加入到这个字符串中,在整体中进行匹配。
re.I
不区分大小写
res = re.findall(r"A", "abc", re.I)
print(res)
['a']
re.M
将所有行的尾字母输出(python3+已经无效)
s = '12 34/n56 78/n90'
re.findall(r'^/d+', s, re.M) # 匹配位于行首的数字 # ['12', '56', '90']
re.findall(r'/A/d+', s, re.M) # 匹配位于字符串开头的数字 # ['12']
re.findall(r'/d+$', s, re.M) # 匹配位于行尾的数字 # ['34', '78', '90']
re.findall(r'/d+/Z', s, re.M) # 匹配位于字符串尾的数字 # ['90']
re.sub
# 要求结果:['12', '23', '34']
l = ['1 2 ', '2 3', ' 3 4']
import re
print(eval(re.sub(r'\s*', '', str(l))))
['12', '23', '34']
使用re.sub()函数将列表l转换为一个整体字符串,并去除其中的空格,使用'\s*'匹配任意数量的空格,并使用空字符串''进行替换。
然后,我们使用re.findall()函数和'\d+'的正则表达式来匹配整体字符串中的数字。'\d+'表示匹配一个或多个数字。
将匹配到的结果存储在result变量中。
最后,使用print()函数打印结果。
结果为['12', '23', '34'],表示处理后的结果中每个元素都没有空格,且为字符串形式的数字。
e.match(pattern, string[, flags])函数(常用)
match,从头匹配一个符合规则的字符串,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
match(pattern, string, flags=0)
- pattern: 正则模型
- string : 要匹配的字符串
- falgs : 匹配模式
注意:match()函数 与 search()函数基本是一样的功能,不一样的就是match()匹配字符串开始位置的一个符合规则的字符串,search()是在字符串全局匹配第一个合规则的字符串
import re
# 无分组
origin = "hello egon bcd egon lge egon acd 19"
r = re.match("h\w+", origin) # match,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
print(r.group()) # 获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来
print(r.groups()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果
print(r.groupdict()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果
hello
()
{}
re.match()函数进行匹配。该函数的第一个参数是要匹配的规则,第二个参数是要进行匹配的字符串。在这个例子中,我们要匹配的规则是"h\w+",表示以字符"h"开头,并且后面跟着一个或多个字母、数字或下划线。
re.match()函数会从字符串的起始位置开始匹配,如果成功匹配到规则,将返回一个匹配对象。如果未匹配成功,将返回None。
接着,我们使用r.group()方法获取匹配对象的匹配结果。该方法返回匹配到的所有结果。
接下来,我们使用r.groups()方法获取匹配对象中的分组结果。由于我们的匹配规则中没有使用分组,因此r.groups()将返回空元组()。
最后,我们使用r.groupdict()方法获取匹配对象中的分组结果,但只返回匹配到的字符串中分组部分定义了key的组结果。由于我们的匹配规则中没有使用分组,因此r.groupdict()将返回空字典{}。
最后,使用print()函数分别打印r.group()、r.groups()和r.groupdict()的结果。
在这个例子中,匹配结果如下:
r.group()的结果是hello,因为这是字符串"hello egon bcd egon lge egon acd 19"中符合匹配规则"h\w+"的部分(以字符"h"开头,后面跟着一个或多个字母、数字或下划线)。
r.groups()的结果是(),因为我们的匹配规则中没有使用分组。
r.groupdict()的结果是{},因为我们的匹配规则中没有使用分组。
# 有分组
# 为何要有分组?提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
r = re.match("h(\w+)", origin) # match,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
print(r.group()) # 获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来
print(r.groups()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果
print(r.groupdict()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果
hello
('ello',)
{}
# 有两个分组定义了key
# 为何要有分组?提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
# ?P<>定义组里匹配内容的key(键),<>里面写key名称,值就是匹配到的内容
r = re.match("(?P<n1>h)(?P<n2>\w+)", origin)
print(r.group()) # 获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来
print(r.groups()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果
print(r.groupdict()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果
hello
('h', 'ello')
{'n1': 'h', 'n2': 'ello'}
分组函数
?P
取出匹配对象方法
只对正则函数返回对象的有用
group() # 获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来,有参取匹配到的第几个如2
groups() # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果
groupdict() # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果
3.5 re.search(pattern, string[, flags])函数
search,浏览全部字符串,匹配第一符合规则的字符串,浏览整个字符串去匹配第一个,未匹配成功返回None
search(pattern, string, flags=0)
pattern: 正则模型
string : 要匹配的字符串
falgs : 匹配模式
注意:match()函数 与 search()函数基本是一样的功能,不一样的就是match()匹配字符串开始位置的一个符合规则的字符串,search()是在字符串全局匹配第一个合规则的字符串
import re
# 无分组
origin = "hello alex bcd alex lge alex acd 19"
# search浏览全部字符串,匹配第一符合规则的字符串,浏览整个字符串去匹配第一个,未匹配成功返回None
r = re.search("a\w+", origin)
print(r.group()) # 获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来
print(r.groups()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果
print(r.groupdict()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果
alex
()
{}
# 有分组
# 为何要有分组?提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
r = re.search("a(\w+).*(\d)", origin)
print(r.group()) # 获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来
print(r.groups()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果
print(r.groupdict()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果
alex bcd alex lge alex acd 19
('lex', '9')
{}
# 有两个分组定义了key
# 为何要有分组?提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
# ?P<>定义组里匹配内容的key(键),<>里面写key名称,值就是匹配到的内容
r = re.search("a(?P<n1>\w+).*(?P<n2>\d)", origin)
print(r.group()) # 获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来
print(r.groups()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果
print(r.groupdict()) # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果
alex bcd alex lge alex acd 19
('lex', '9')
{'n1': 'lex', 'n2': '9'}
re.findall(pattern, string[, flags])函数(常用)
findall(pattern, string, flags=0)
- pattern: 正则模型
- string : 要匹配的字符串
- falgs : 匹配模式
浏览全部字符串,匹配所有合规则的字符串,匹配到的字符串放到一个列表中,未匹配成功返回空列表
注意:一旦匹配成,再次匹配,是从前一次匹配成功的,后面一位开始的,也可以理解为匹配成功的字符串,不在参与下次匹配
import re
# 无分组
r = re.findall("\d+\w\d+", "a2b3c4d5") # 浏览全部字符串,匹配所有合规则的字符串,匹配到的字符串放到一个列表中
print(r) # 注意:匹配成功的字符串,不在参与下次匹配,所以3c4也符合规则但是没匹配到
['2b3', '4d5']
注意:如果没写匹配规则,也就是空规则,返回的是一个比原始字符串多一位的,空字符串列表
import re
# 无分组
r = re.findall("", "a2b3c4d5") # 浏览全部字符串,匹配所有合规则的字符串,匹配到的字符串放到一个列表中
print(r) # 注意:如果没写匹配规则,也就是空规则,返回的是一个比原始字符串多一位的,空字符串列表
['', '', '', '', '', '', '', '', '']
注意:正则匹配到空字符的情况,如果规则里只有一个组,而组后面是就表示组里的内容可以是0个或者多过,这样组里就有了两个意思,一个意思是匹配组里的内容,二个意思是匹配组里0内容(即是空白)所以尽量避免用否则会有可能匹配出空字符串
注意:正则只拿组里最后一位,如果规则里只有一个组,匹配到的字符串里在拿组内容是,拿的是匹配到的内容最后一位
import re
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("(a)*", origin)
print(r)
['', '', '', '', '', '', 'a', '', '', '', '', '', '', '', '', 'a', '', '', '', '', '', '', '', '', 'a', '', '', '', '', 'a', '', '', '', '', '', '']
无分组:匹配所有合规则的字符串,匹配到的字符串放到一个列表中
import re
# 无分组
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("a\w+", origin) # 浏览全部字符串,匹配所有合规则的字符串,匹配到的字符串放到一个列表中
print(r)
['alex', 'alex', 'alex', 'acd']
有分组:只将匹配到的字符串里,组的部分放到列表里返回,相当于groups()方法
import re
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("a(\w+)", origin) # 有分组:只将匹配到的字符串里,组的部分放到列表里返回
print(r)
['lex', 'lex', 'lex', 'cd']
多个分组:只将匹配到的字符串里,组的部分放到一个元组中,最后将所有元组放到一个列表里返
相当于在group()结果里再将组的部分,分别,拿出来放入一个元组,最后将所有元组放入一个列表返回
import re
origin = "hello alex bcd alex lge alex acd 19"
# 多个分组:只将匹配到的字符串里,组的部分放到一个元组中,最后将所有元组放到一个列表里返回
r = re.findall("(a)(\w+)", origin)
print(r)
[('a', 'lex'), ('a', 'lex'), ('a', 'lex'), ('a', 'cd')]
分组中有分组:只将匹配到的字符串里,组的部分放到一个元组中,先将包含有组的组,看作一个整体也就是一个组,把这个整体组放入一个元组里,然后在把组里的组放入一个元组,最后将所有组放入一个列表返回
import re
origin = "hello alex bcd alex lge alex acd 19"
# 分组中有分组:只将匹配到的字符串里,组的部分放到一个元组中,先将包含有组的组,看作一个整体也就是一个组,把这个整体组放入一个元组里,然后在把组里的组放入一个元组,最后将所有组放入一个列表返回
r = re.findall("(a)(\w+(e))", origin)
print(r)
[('a', 'le', 'e'), ('a', 'le', 'e'), ('a', 'le', 'e')]
?:在有分组的情况下findall()函数,不只拿分组里的字符串,拿所有匹配到的字符串,注意?:只用于不是返回正则对象的函数如findall()
import re
origin = "hello alex bcd alex lge alex acd 19"
# ?:在有分组的情况下,不只拿分组里的字符串,拿所有匹配到的字符串,注意?:只用于不是返回正则对象的函数如findall()
b = re.findall("a(?:\w+)", origin)
print(b)
['alex', 'alex', 'alex', 'acd']
re.split(pattern, string[, maxsplit])函数
根据正则匹配分割字符串,返回分割后的一个列表
split(pattern, string, maxsplit=0, flags=0)
- pattern: 正则模型
- string : 要匹配的字符串
- maxsplit:指定分割个数
- flags : 匹配模式
按照一个字符将全部字符串进行分割
import re
origin = "hello alex bcd alex lge alex acd 19"
r = re.split("a", origin) # 根据正则匹配分割字符串
print(r)
['hello ', 'lex bcd ', 'lex lge ', 'lex ', 'cd 19']
将匹配到的字符串作为分割标准进行分割
import re
origin = "hello alex bcd alex lge alex 2acd 19"
r = re.split("a\w+", origin) # 根据正则匹配分割字符串
print(r)
['hello ', ' bcd ', ' lge ', ' 2', ' 19']
re.sub(pattern, repl, string[, count])函数
替换匹配成功的指定位置字符串
sub(pattern, repl, string, count=0, flags=0)
- pattern: 正则模型
- repl : 要替换的字符串
- string : 要匹配的字符串
- count : 指定匹配个数
- flags : 匹配模式
import re
origin = "hello alex bcd alex lge alex acd 19"
r = re.sub("a", "替换", origin) # 替换匹配成功的指定位置字符串
print(r)
hello 替换lex bcd 替换lex lge 替换lex 替换cd 19
# re.sub 和分组命名一起使用
s = '''在PyTorch调用CuDNN报错时'''
# (?P<a>[A-z]+)把[A-z]+这个匹配模式命名为“a”,然后通过\g<a>这个语法把命名为“a”的匹配结果拿出来使用
s = re.sub('(?P<a>[A-z]+)', ' \g<a> ', s) # 对英文字母两边添加空格
print(s) # 在 PyTorch 调用 CuDNN 报错时
在 PyTorch 调用 CuDNN 报错时
re.subn(pattern, repl, string,[, count][, flags])函数
替换匹配成功的指定位置字符串,并且返回替换次数,可以用两个变量分别接受
subn(pattern, repl, string, count=0, flags=0)
- pattern: 正则模型
- repl : 要替换的字符串
- string : 要匹配的字符串
- count : 指定匹配个数
- flags : 匹配模式
import re
origin = "hello alex bcd alex lge alex acd 19"
a, b = re.subn("a", "替换", origin) # 替换匹配成功的指定位置字符串,并且返回替换次数,可以用两个变量分别接受
print(a)
print(b)
hello 替换lex bcd 替换lex lge 替换lex 替换cd 19
4
注意事项
-
r原生字符:让在python里有特殊意义的字符如\b,转换成原生字符(就是去除它在python的特殊意义),不然会给正则表达式有冲突,为了避免这种冲突可以在规则前加原始字符r
-
正则表达式,返回类型为表达式对象的,如:<_sre.SRE_Match object; span=(6, 7), match='a'>,返回对象时,需要用正则方法取字符串,方法有:
- group() # 获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来,有参取匹配到的第几个如2
- groups() # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果
- groupdict() # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果
-
匹配到的字符串里出现空字符:注意:正则匹配到空字符的情况,如果规则里只有一个组,而组后面是就表示组里的内容可以是0个或者多过,这样组里就有了两个意思,一个意思是匹配组里的内容,二个意思是匹配组里0内容(即是空白)所以尽量避免用否则会有可能匹配出空字符串
-
()分组:注意:分组的意义,就是在匹配成功的字符串中,再提取()里的内容,也就是组里面的字符串
-
?:在有分组的情况下findall()函数,不只拿分组里的字符串,拿所有匹配到的字符串,注意?:只用于不是返回正则对象的函数如findall()
计算器(经典)
基于递归和正则将下面的字符串翻译成计算器表达式,并且获取最终结果:expression='-1-2\*((60+2\*(-3-40.0+42425/5)\*(9-2\*5/3+357/553/3\*99/4\*2998+10\*568/14))-(-4\*3)/(16-3\*2))+56+(56-45)'
如果代码正确,计算结果为:-553071849.7670887
提示:content=re.search('((\[-+\\*/\]\*\d+.?\d\*)+)',expression).group() #(-3-40.0/5)
复杂版本
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
该计算器思路:
1、递归寻找表达式中只含有 数字和运算符的表达式,并计算结果
2、由于整数计算会忽略小数,所有的数字都认为是浮点型操作,以此来保留小数
使用技术:
1、正则表达式
2、递归
"""
import re
def compute_mul_div(arg):
""" 操作乘除
:param expression:表达式
:return:计算结果
"""
val = arg[0]
mch = re.search('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*', val)
if not mch:
return
content = re.search('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*', val).group()
if len(content.split('*')) > 1:
n1, n2 = content.split('*')
value = float(n1) * float(n2)
else:
n1, n2 = content.split('/')
value = float(n1) / float(n2)
before, after = re.split('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*', val, 1)
new_str = "%s%s%s" % (before, value, after)
arg[0] = new_str
compute_mul_div(arg)
def compute_add_sub(arg):
""" 操作加减
:param expression:表达式
:return:计算结果
"""
while True:
if arg[0].__contains__('+-') or arg[0].__contains__("++") or arg[
0].__contains__('-+') or arg[0].__contains__("--"):
arg[0] = arg[0].replace('+-', '-')
arg[0] = arg[0].replace('++', '+')
arg[0] = arg[0].replace('-+', '-')
arg[0] = arg[0].replace('--', '+')
else:
break
if arg[0].startswith('-'):
arg[1] += 1
arg[0] = arg[0].replace('-', '&')
arg[0] = arg[0].replace('+', '-')
arg[0] = arg[0].replace('&', '+')
arg[0] = arg[0][1:]
val = arg[0]
mch = re.search('\d+\.*\d*[\+\-]{1}\d+\.*\d*', val)
if not mch:
return
content = re.search('\d+\.*\d*[\+\-]{1}\d+\.*\d*', val).group()
if len(content.split('+')) > 1:
n1, n2 = content.split('+')
value = float(n1) + float(n2)
else:
n1, n2 = content.split('-')
value = float(n1) - float(n2)
before, after = re.split('\d+\.*\d*[\+\-]{1}\d+\.*\d*', val, 1)
new_str = "%s%s%s" % (before, value, after)
arg[0] = new_str
compute_add_sub(arg)
def compute(expression):
""" 操作加减乘除
:param expression:表达式
:return:计算结果
"""
inp = [expression, 0]
# 处理表达式中的乘除
compute_mul_div(inp)
# 处理
compute_add_sub(inp)
if divmod(inp[1], 2)[1] == 1:
result = float(inp[0])
result = result * -1
else:
result = float(inp[0])
return result
def exec_bracket(expression):
""" 递归处理括号,并计算
:param expression: 表达式
:return:最终计算结果
"""
# 如果表达式中已经没有括号,则直接调用负责计算的函数,将表达式结果返回,如:2*1-82+444
if not re.search('\(([\+\-\*\/]*\d+\.*\d*){2,}\)', expression):
final = compute(expression)
return final
# 获取 第一个 只含有 数字/小数 和 操作符 的括号
# 如:
# ['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
# 找出:(-40.0/5)
content = re.search('\(([\+\-\*\/]*\d+\.*\d*){2,}\)', expression).group()
# 分割表达式,即:
# 将['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
# 分割更三部分:['1-2*((60-30+( (-40.0/5) *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
before, nothing, after = re.split('\(([\+\-\*\/]*\d+\.*\d*){2,}\)',
expression, 1)
print('before:', expression)
content = content[1:len(content) - 1]
# 计算,提取的表示 (-40.0/5),并活的结果,即:-40.0/5=-8.0
ret = compute(content)
print('%s=%s' % (content, ret))
# 将执行结果拼接,['1-2*((60-30+( -8.0 *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
expression = "%s%s%s" % (before, ret, after)
print('after:', expression)
print("=" * 10, '上一次计算结束', "=" * 10)
# 循环继续下次括号处理操作,本次携带者的是已被处理后的表达式,即:
# ['1-2*((60-30+ -8.0 *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
# 如此周而复始的操作,直到表达式中不再含有括号
return exec_bracket(expression)
# 使用 __name__ 的目的:
# 只有执行 python index.py 时,以下代码才执行
# 如果其他人导入该模块,以下代码不执行
if __name__ == "__main__":
print(
'*' * 20, "请计算表达式:",
"1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )",
'*' * 20)
# inpp = '1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) '
inpp = '-1-2*((60+2*(-3-40.0+42425/5)*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)'
# inpp = "1-2*-30/-12*(-20+200*-3/-200*-300-100)"
# inpp = "1-5*980.0"
inpp = re.sub('\s*', '', inpp)
# 表达式保存在列表中
result = exec_bracket(inpp)
print(result)
******************** 请计算表达式: 1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) ********************
before: -1-2*((60+2*(-3-40.0+42425/5)*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)
-3-40.0+42425/5=8442.0
after: -1-2*((60+2*8442.0*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)
========== 上一次计算结束 ==========
before: -1-2*((60+2*8442.0*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)
9-2*5/3+357/553/3*99/4*2998+10*568/14=16378.577154912598
after: -1-2*((60+2*8442.0*16378.577154912598)-(-4*3)/(16-3*2))+56+(56-45)
========== 上一次计算结束 ==========
before: -1-2*((60+2*8442.0*16378.577154912598)-(-4*3)/(16-3*2))+56+(56-45)
60+2*8442.0*16378.577154912598=276535956.68354434
after: -1-2*(276535956.68354434-(-4*3)/(16-3*2))+56+(56-45)
========== 上一次计算结束 ==========
before: -1-2*(276535956.68354434-(-4*3)/(16-3*2))+56+(56-45)
-4*3=-12.0
after: -1-2*(276535956.68354434--12.0/(16-3*2))+56+(56-45)
========== 上一次计算结束 ==========
before: -1-2*(276535956.68354434--12.0/(16-3*2))+56+(56-45)
16-3*2=10.0
after: -1-2*(276535956.68354434--12.0/10.0)+56+(56-45)
========== 上一次计算结束 ==========
before: -1-2*(276535956.68354434--12.0/10.0)+56+(56-45)
276535956.68354434--12.0/10.0=276535957.8835443
after: -1-2*276535957.8835443+56+(56-45)
========== 上一次计算结束 ==========
before: -1-2*276535957.8835443+56+(56-45)
56-45=11.0
after: -1-2*276535957.8835443+56+11.0
========== 上一次计算结束 ==========
-553071849.7670887
简易版本
import re
expression = '-1-2*((60+2*(-3-40.0+42425/5)*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)'
question = eval(expression)
print(question)
def arithmetic(expression='1+1'):
# content = re.search('\(([\-\+\*\/]*\d+\.?\d*)+\)', expression) # (-3-40.0/5)
content = re.search('\(([-+*/]*\d+\.?\d*)+\)', expression) # (-3-40.0/5)
if content:
content = content.group()
content = content[1:-1]
print('content:', content)
replace_content = next_arithmetic(content)
expression = re.sub('\(([-+*/]*\d+\.?\d*)+\)',
replace_content,
expression,
count=1)
print('next_expression:', expression)
else:
answer = next_arithmetic(expression)
return answer
return arithmetic(expression)
def next_arithmetic(content):
while True:
next_content_mul_div = re.search('\d+\.?\d*[*/][-+]?\d+\.?\d*',
content) # 找出带有*/的式子
if next_content_mul_div: # 如果content含有带有*/的式子
next_content_mul_div = next_content_mul_div.group()
print('next_content_mul_div:', next_content_mul_div)
mul_div_content = mul_div(next_content_mul_div) # 计算出带有*/的式子
print('mul_div_content:', mul_div_content)
content = re.sub('\d+\.?\d*[*/][-+]?\d+\.?\d*',
str(mul_div_content),
content,
count=1) # 把带有*/的式子计算出来后替换掉
print('content:', content)
continue
next_content_add_sub = re.search('-?\d+\.?\d*[-+][-+]?\d+\.?\d*',
content) # 找出带有-+的式子
if next_content_add_sub: # 如果content含有带有+-的式子
next_content_add_sub = next_content_add_sub.group()
print('next_content_add_sub:', next_content_add_sub)
add_sub_content = add_sub(next_content_add_sub) # 计算出带有-+的式子
print('add_sub_content:', add_sub_content)
add_sub_content = str(add_sub_content)
content = re.sub('-?\d+\.?\d*[-+]-?\d+\.?\d*',
str(add_sub_content),
content,
count=1) # 把带有-+的式子计算出来后替换掉
print('content:', content)
continue
else:
break
return content
def add_sub(content):
if '+' in content:
content = content.split('+')
print(content)
content = float(content[0]) + float(content[1])
return content
elif '-' in content:
content = content.split('-')
# 减法情况有多种
if content[0] == '-' and content[2] == '-':
# content = content.split('-')
print(content)
content = -float(content[1]) - float(content[-1])
return content
if content[0] == '-':
# content = content.split('-')
print(content)
content = -float(content[1]) - float(content[-1])
return content
if content[1] == '-' and content[2] == '-':
# content = content.split('-')
print(content)
content = -float(content[0]) + float(content[-1])
return content
if content[1] == '':
# content = content.split('-')
print(content)
content = float(content[0]) - float(content[2])
return content
if content[0] == '' and content[2] != '':
print(content)
content = -float(content[1]) - float(content[2])
return content
if content[0] == '' and content[2] == '':
print(content)
content = -float(content[1]) + float(content[3])
return content
else:
# content = content.split('-')
print(content)
content = float(content[0]) - float(content[1])
return content
def mul_div(content):
if '*' in content:
content = content.split('*')
print(content)
content = float(content[0]) * float(content[1])
return content
elif '/' in content:
content = content.split('/')
print(content)
content = float(content[0]) / float(content[1])
return content
# 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))'
expression = '-1-2*((60+2*(-3-40.0+42425/5)*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)'
answer = arithmetic(expression)
print(answer)
-553071849.7670887
content: -3-40.0+42425/5
next_content_mul_div: 42425/5
['42425', '5']
mul_div_content: 8485.0
content: -3-40.0+8485.0
next_content_add_sub: -3-40.0
['', '3', '40.0']
add_sub_content: -43.0
content: -43.0+8485.0
next_content_add_sub: -43.0+8485.0
['-43.0', '8485.0']
add_sub_content: 8442.0
content: 8442.0
next_expression: -1-2*((60+2*8442.0*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)
content: 9-2*5/3+357/553/3*99/4*2998+10*568/14
next_content_mul_div: 2*5
['2', '5']
mul_div_content: 10.0
content: 9-10.0/3+357/553/3*99/4*2998+10*568/14
next_content_mul_div: 10.0/3
['10.0', '3']
mul_div_content: 3.3333333333333335
content: 9-3.3333333333333335+357/553/3*99/4*2998+10*568/14
next_content_mul_div: 357/553
['357', '553']
mul_div_content: 0.6455696202531646
content: 9-3.3333333333333335+0.6455696202531646/3*99/4*2998+10*568/14
next_content_mul_div: 0.6455696202531646/3
['0.6455696202531646', '3']
mul_div_content: 0.21518987341772153
content: 9-3.3333333333333335+0.21518987341772153*99/4*2998+10*568/14
next_content_mul_div: 0.21518987341772153*99
['0.21518987341772153', '99']
mul_div_content: 21.303797468354432
content: 9-3.3333333333333335+21.303797468354432/4*2998+10*568/14
next_content_mul_div: 21.303797468354432/4
['21.303797468354432', '4']
mul_div_content: 5.325949367088608
content: 9-3.3333333333333335+5.325949367088608*2998+10*568/14
next_content_mul_div: 5.325949367088608*2998
['5.325949367088608', '2998']
mul_div_content: 15967.196202531646
content: 9-3.3333333333333335+15967.196202531646+10*568/14
next_content_mul_div: 10*568
['10', '568']
mul_div_content: 5680.0
content: 9-3.3333333333333335+15967.196202531646+5680.0/14
next_content_mul_div: 5680.0/14
['5680.0', '14']
mul_div_content: 405.7142857142857
content: 9-3.3333333333333335+15967.196202531646+405.7142857142857
next_content_add_sub: 9-3.3333333333333335
['9', '3.3333333333333335']
add_sub_content: 5.666666666666666
content: 5.666666666666666+15967.196202531646+405.7142857142857
next_content_add_sub: 5.666666666666666+15967.196202531646
['5.666666666666666', '15967.196202531646']
add_sub_content: 15972.862869198312
content: 15972.862869198312+405.7142857142857
next_content_add_sub: 15972.862869198312+405.7142857142857
['15972.862869198312', '405.7142857142857']
add_sub_content: 16378.577154912598
content: 16378.577154912598
next_expression: -1-2*((60+2*8442.0*16378.577154912598)-(-4*3)/(16-3*2))+56+(56-45)
content: 60+2*8442.0*16378.577154912598
next_content_mul_div: 2*8442.0
['2', '8442.0']
mul_div_content: 16884.0
content: 60+16884.0*16378.577154912598
next_content_mul_div: 16884.0*16378.577154912598
['16884.0', '16378.577154912598']
mul_div_content: 276535896.68354434
content: 60+276535896.68354434
next_content_add_sub: 60+276535896.68354434
['60', '276535896.68354434']
add_sub_content: 276535956.68354434
content: 276535956.68354434
next_expression: -1-2*(276535956.68354434-(-4*3)/(16-3*2))+56+(56-45)
content: -4*3
next_content_mul_div: 4*3
['4', '3']
mul_div_content: 12.0
content: -12.0
next_expression: -1-2*(276535956.68354434--12.0/(16-3*2))+56+(56-45)
content: 16-3*2
next_content_mul_div: 3*2
['3', '2']
mul_div_content: 6.0
content: 16-6.0
next_content_add_sub: 16-6.0
['16', '6.0']
add_sub_content: 10.0
content: 10.0
next_expression: -1-2*(276535956.68354434--12.0/10.0)+56+(56-45)
content: 276535956.68354434--12.0/10.0
next_content_mul_div: 12.0/10.0
['12.0', '10.0']
mul_div_content: 1.2
content: 276535956.68354434--1.2
next_content_add_sub: 276535956.68354434--1.2
['276535956.68354434', '', '1.2']
add_sub_content: 276535955.48354435
content: 276535955.48354435
next_expression: -1-2*276535955.48354435+56+(56-45)
content: 56-45
next_content_add_sub: 56-45
['56', '45']
add_sub_content: 11.0
content: 11.0
next_expression: -1-2*276535955.48354435+56+11.0
next_content_mul_div: 2*276535955.48354435
['2', '276535955.48354435']
mul_div_content: 553071910.9670887
content: -1-553071910.9670887+56+11.0
next_content_add_sub: -1-553071910.9670887
['', '1', '553071910.9670887']
add_sub_content: -553071911.9670887
content: -553071911.9670887+56+11.0
next_content_add_sub: -553071911.9670887+56
['-553071911.9670887', '56']
add_sub_content: -553071855.9670887
content: -553071855.9670887+11.0
next_content_add_sub: -553071855.9670887+11.0
['-553071855.9670887', '11.0']
add_sub_content: -553071844.9670887
content: -553071844.9670887
-553071844.9670887
typing模块
作用:
- 类型检查,防止运行时出现参数和返回值类型不符合。
- 作为开发文档附加说明,方便使用者调用时传入和返回参数类型。
- 该模块加入后并不会影响程序的运行,不会报正式的错误,只有提醒。
注意:typing模块只有在python3.5以上的版本中才可以使用,pycharm目前支持typing检查
from typing import List, Tuple, Dict
def add(a: int, string: str, f: float,
b: bool) -> Tuple[List, Tuple, Dict, bool]:
list1 = list(range(a))
tup = (string, string, string)
d = {"a": f}
bl = b
return list1, tup, d, bl
print(add(5, "hhhh", 2.3, False))
([0, 1, 2, 3, 4], ('hhhh', 'hhhh', 'hhhh'), {'a': 2.3}, False)
- 在传入参数时通过"参数名:类型"的形式声明参数的类型;
- 返回结果通过"-> 结果类型"的形式声明结果的类型。
- 在调用的时候如果参数的类型不正确pycharm会有提醒,但不会影响程序的运行。
- 对于如list列表等,还可以规定得更加具体一些,如:"-> List[str]”,规定返回的是列表,并且元素是字符串。
from typing import List
def func(a: int, string: str) -> List[int or str]: # 使用or关键字表示多种类型
list1 = []
list1.append(a)
list1.append(string)
return list1
常用类型
- int、long、float: 整型、长整形、浮点型
- ibool、str: 布尔型、字符串类型
- iList、 Tuple、 Dict、 Set:列表、元组、字典、集合
- iIterable、Iterator:可迭代类型、迭代器类型
- iGenerator:生成器类型
collections模块
collections是Python内建的一个集合模块,提供了许多有用的集合类。
namedtuple
我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:
p = (1,2)
但是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。
定义一个class又小题大做了,这时,namedtuple就派上了用场:
from collections import namedtuple
Point = namedtuple('Point',['x','y'])
p = Point(1,2)
p.x
1
p.y
2
namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。
这样一来,我们用namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用,使用十分方便。
可以验证创建的Point对象是tuple的一种子类:
isinstance(p,Point)
True
isinstance(p, tuple)
True
类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:
# namedtuple('名称', [属性list]):
Circle = namedtuple('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(),这样就可以非常高效地往头部添加或删除元素。
defaultdict
使用dict时,如果引用的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'
注意默认值是调用函数返回的,而函数在创建defaultdict对象时传入。
除了在Key不存在时返回默认值,defaultdict的其他行为跟dict是完全一样的。
OrderedDict
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。
如果要保持Key的顺序,可以用OrderedDict:
from collections import OrderedDict
d = dict([('a', 1), ('b', 2), ('c', 3)])
d # dict的Key是无序的
{'a': 1, 'b': 2, 'c': 3}
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序:
od = OrderedDict()
od['z'] = 1
od['y'] = 2
od['x'] = 3
od.keys() # 按照插入的Key的顺序返回
odict_keys(['z', 'y', 'x'])
OrderedDict可以实现一个FIFO(先进先出)的dict,当容量超出限制时,先删除最早添加的Key:
from collections import OrderedDict
class LastUpdatedOrderedDict(OrderedDict):
def __init__(self, capacity):
super(LastUpdatedOrderedDict, self).__init__()
self._capacity = capacity
def __setitem__(self, key, value):
containsKey = 1 if key in self else 0
if len(self) - containsKey >= self._capacity:
last = self.popitem(last=False)
print('remove:', last)
if containsKey:
del self[key]
print('set:', (key, value))
else:
print('add:', (key, value))
OrderedDict.__setitem__(self, key, value)
Counter
Counter是一个简单的计数器,例如,统计字符出现的个数:
from collections import Counter
c = Counter()
for ch in 'programming':
c[ch] = c[ch] + 1
Counter实际上也是dict的一个子类,上面的结果可以看出,字符'g'、'm'、'r'各出现了两次,其他字符各出现了一次。
pathlib模块
相对于 os 模块的 path 方法,Python3 标准库 pathlib 模块的 Path 对路径的操作会更简单。
shutil模块(高级的文件、文件夹、压缩包处理模块。)
import shutil
# shutil.copyfileobj(fsrc, fdst[, length]),将文件内容拷贝到另一个文件中
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.copystat(src, dst),仅拷贝状态的信息,包括:mode bits, atime, mtime, flags
shutil.copystat('f1.log', 'f2.log') # 目标文件必须存在
# shutil.copy(src, dst),拷贝文件和权限
shutil.copy('f1.log', 'f2.log')
# shutil.copy2(src, dst),拷贝文件和状态信息
shutil.copy2('f1.log', 'f2.log')
# shutil.ignore_patterns(*patterns)
# shutil.copytree(src, dst, symlinks=False, ignore=None),递归的去拷贝文件夹
# 目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
# shutil.rmtree(path[, ignore_errors[, one rror]]),递归的去删除文件
shutil.rmtree('folder1')
# shutil.move(src, dst),递归的去移动文件,它类似mv命令,其实就是重命名
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”
root_dir:要压缩的文件夹路径(默认当前目录)
owner:用户,默认当前用户
group:组,默认当前组
logger:用于记录日志,通常是logging.Logger对象
'''
# 将 /data 下的文件打包放置当前程序目录
import shutil
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')
# 将 /data下的文件打包放置 /tmp/目录
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')
zipfile压缩解压缩
# shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:
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()
xml模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。
xml的格式如下,就是通过<>节点来区别数据结构的:
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
Python使用xml
xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:
# print(root.iter('year')) #全文搜索
# print(root.find('country')) #在root的子节点找,只找一个
# print(root.findall('country')) #在root的子节点找,找所有
import xml.etree.ElementTree as ET
tree = ET.parse("xmltest.xml")
root = tree.getroot()
print(root.tag)
#遍历xml文档
for child in root:
print('========>', child.tag, child.attrib, child.attrib['name'])
for i in child:
print(i.tag, i.attrib, i.text)
#只遍历year 节点
for node in root.iter('year'):
print(node.tag, node.text)
#---------------------------------------
import xml.etree.ElementTree as ET
tree = ET.parse("xmltest.xml")
root = tree.getroot()
#修改
for node in root.iter('year'):
new_year = int(node.text) + 1
node.text = str(new_year)
node.set('updated', 'yes')
node.set('version', '1.0')
tree.write('test.xml')
#删除node
for country in root.findall('country'):
rank = int(country.find('rank').text)
if rank > 50:
root.remove(country)
tree.write('output.xml')
#在country内添加(append)节点year2
import xml.etree.ElementTree as ET
tree = ET.parse("a.xml")
root = tree.getroot()
for country in root.findall('country'):
for year in country.findall('year'):
if int(year.text) > 2000:
year2 = ET.Element('year2')
year2.text = '新年'
year2.attrib = {'update': 'yes'}
country.append(year2) #往country节点下添加子节点
tree.write('a.xml.swap')
自己创建xml文档
import xml.etree.ElementTree as ET
new_xml = ET.Element("namelist")
name = ET.SubElement(new_xml, "name", attrib={"enrolled": "yes"})
age = ET.SubElement(name, "age", attrib={"checked": "no"})
sex = ET.SubElement(name, "sex")
sex.text = '33'
name2 = ET.SubElement(new_xml, "name", attrib={"enrolled": "no"})
age = ET.SubElement(name2, "age")
age.text = '19'
et = ET.ElementTree(new_xml) #生成文档对象
et.write("test.xml", encoding="utf-8", xml_declaration=True)
ET.dump(new_xml) #打印生成的格式
subprocess模块
subprocess模块允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等。更多查看官网:https://docs.python.org/2/library/subprocess.html?highlight=subprocess#frequently-used-arguments
import subprocess
import subprocess
'''
sh-3.2# ls /Users/nick/Desktop |grep txt$
mysql.txt
tt.txt
事物.txt
'''
res1 = subprocess.Popen('ls /Users/jieli/Desktop',
shell=True,
stdout=subprocess.PIPE)
res = subprocess.Popen('grep txt$',
shell=True,
stdin=res1.stdout,
stdout=subprocess.PIPE)
print(res.stdout.read().decode('utf-8'))
# 等同于上面,但是上面的优势在于,一个数据流可以和另外一个数据流交互,可以通过爬虫得到结果然后交给grep
res1 = subprocess.Popen('ls /Users/jieli/Desktop |grep txt$',
shell=True,
stdout=subprocess.PIPE)
print(res1.stdout.read().decode('utf-8'))
# windows下:
# dir | findstr 'test*'
# dir | findstr 'txt$'
res1 = subprocess.Popen(r'dirC:\Users\Administrator\PycharmProjects\test\函数备课',
shell=True,
stdout=subprocess.PIPE)
res = subprocess.Popen('findstr test*',
shell=True,
stdin=res1.stdout,
stdout=subprocess.PIPE)
# subprocess使用当前系统默认编码,得到结果为bytes类型,在windows下需要用gbk解码
print(res.stdout.read().decode('gbk'))
标签:day09,匹配,arr,content,re,print,numpy
From: https://www.cnblogs.com/qingchuan/p/17595230.html