Python的常用模块

python的常用模块

一、time模块

# time模块
#     python中时间分为三种格式:
#     1、时间戳(timestamp):从1970年到现在经过的秒数
#           作用:用于时间间隔的计算
import time
print(time.time())

#     2、按某种格式显示的时间(Format String):2020-04-15 09:09:09
#       作用:用于展示时间
print(time.strftime('%Y-%m-%d %H:%M:%S %p'))  # 2020-04-15 09:20:27 AM
print(time.strftime('%Y-%m-%d %X'))  # 2020-04-15 09:20:48
print(time.strftime('%Y-%m-%d %Y'))  # 2020-04-15 2020

#     3、结构化的时间(struct_time)
#       作用:用于单独获取时间的某一部分
res = time.localtime()
print(res)
"""运行结果:
time.struct_time(tm_year=2020, tm_mon=4, tm_mday=15, tm_hour=9, 
                tm_min=27, tm_sec=6, tm_wday=2, tm_yday=106, tm_isdst=0)
tm_year=2020 当前年
tm_mon=4     当前月
tm_mday=15   当前天
tm_hour=9    当前小时
tm_min=27    当前分钟
tm_sec=6     当前秒
tm_wday=2    当前是周几,从0开始:0代表星期一
tm_yday=106  当前所在年的天:当前年的第106天。
tm_isdst=0   是否是夏令时
"""
print(res.tm_year)  # 获取当前年-->2020
print(res.tm_yday)  # 当前所在年的天-->106
%a    Locale’s abbreviated weekday name.     
%A    Locale’s full weekday name.     
%b    Locale’s abbreviated month name.     
%B    Locale’s full month name.     
%c    Locale’s appropriate date and time representation.     
%d    Day of the month as a decimal number [01,31].     
%H    Hour (24-hour clock) as a decimal number [00,23].     
%I    Hour (12-hour clock) as a decimal number [01,12].     
%j    Day of the year as a decimal number [001,366].     
%m    Month as a decimal number [01,12].     
%M    Minute as a decimal number [00,59].     
%p    Locale’s equivalent of either AM or PM.    (1)
%S    Second as a decimal number [00,61].    (2)
%U    Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0.    (3)
%w    Weekday as a decimal number [0(Sunday),6].     
%W    Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0.    (3)
%x    Locale’s appropriate date representation.     
%X    Locale’s appropriate time representation.     
%y    Year without century as a decimal number [00,99].     
%Y    Year with century as a decimal number.     
%z    Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59].     
%Z    Time zone name (no characters if no time zone exists).     
%%    A literal '%' character.
time模块内%含义

 二、datetime模块

import datetime
# 获取当前时间
print(datetime.datetime.now())  # 2020-04-15 09:41:48.551997
# 在此时间基础上参与时间的运算:当前时间+3天后的时间。3可以是正数也可以是负数。
print(datetime.datetime.now() + datetime.timedelta(days=3))  # 2020-04-18 09:47:26.094479

"""timedelta(days=)语法
def __init__(self, days: float = ..., seconds: float = ..., microseconds: float = ...,
             milliseconds: float = ..., minutes: float = ..., hours: float = ...,
             weeks: float = ...) -> None: ...
"""

 三、时间格式的转换

计算机认识的时间只能是“时间戳”格式。
人类能读懂的时间有:格式化的字符串时间 ,机构化的时间
三种转换关系如下图所示

import time
# 时间模块需要掌握的操作
# 1、时间格式的转换
# 结构化时间struct_time --> 时间戳Timestamp
# s_time = time.localtime()
# print(time.mktime(s_time))  # 1586916748.0
# print(time.time())          # 1586916748.0209408

# 时间戳Timestamp --> 结构化时间:struct_time
# tp_time = time.time()
# print(time.localtime(tp_time))
# time.struct_time(tm_year=2020, tm_mon=4, tm_mday=15, tm_hour=10, tm_min=13, tm_sec=38, tm_wday=2, tm_yday=106, tm_isdst=0)

# 结构化时间struct_time -->格式化的字符串形式的时间
s_time = time.localtime()
print(time.strftime('%Y-%m-%d %H:%M:%S',s_time))  # 2020-04-15 10:27:20

# 格式化的字符串时间-->结构化时间struct_time
print(time.strptime('2020-04-15 10:27:20','%Y-%m-%d %H:%M:%S'))
# time.struct_time(tm_year=2020, tm_mon=4, tm_mday=15, tm_hour=10, tm_min=27, tm_sec=20, tm_wday=2, tm_yday=106, tm_isdst=-1)
# 重点掌握:格式化的字符串时间format string <--> 时间戳timestamp,互相转换,通过结构化中转
# 假设需求:格式化字符串时间是2020-04-15 10:27:20,该时间是VIP注册的时间,用户又充值7天时间
# 1、先将格式化字符串时间转换成结构化时间(forma string --> struct_time)
struct_time = time.strptime('2020-04-15 10:27:20','%Y-%m-%d %H:%M:%S')
# 2、将结构化时间转换成时间戳(struct_time-->timestamp,一天是时间是=24*60*60=86400秒),加7天时间
timestamp = time.mktime(struct_time) + 7*86400
print(timestamp)  # 1587522440.0
# 3、将时间戳转换成结构化时间(timestamp-->struct_time)
struct_time_1 = time.localtime(timestamp)
# 4、将结构化时间转换成格式化字符串时间(struct_time --> Format String)
format_string = time.strftime('%Y-%m-%d %H:%M:%S',struct_time_1)
print(format_string)  # 2020-04-22 10:27:20
# 直接将时间戳直接转换成格式化字符串的时间
print(datetime.datetime.fromtimestamp(333333)) # 1970-01-05 04:35:33 
# 补充:世界标准时间与本地时间
# print(time.localtime())  # 本地时间,tm_hour本地时间与世界标准时间相差8小时
# print(time.gmtime())  # 世界标准时间  # tm_hour=2世界标准时间
# print(time.localtime(333333333))  # tm_hour=8
# print(time.gmtime(333333333))     # tm_hour=0
# import time
# 了解知识
# 休眠
# time.sleep(3)

# time.strftime()与time.asctime()区别,不用%
# print(time.asctime())  # Wed Apr 15 12:39:50 2020
# print(time.strftime())

import datetime
# 获取当前时间
print(datetime.datetime.now())  # 2020-04-15 12:43:04.573067
print(datetime.datetime.utcnow()) # 2020-04-15 04:43:04.573067

四、random模块

 1 import random
 2  
 3 print(random.random())#(0,1)----float    大于0且小于1之间的小数
 4  
 5 print(random.randint(1,3))  #[1,3]    大于等于1且小于等于3之间的整数
 6  
 7 print(random.randrange(1,3)) #[1,3)    大于等于1且小于3之间的整数
 8  
 9 print(random.choice([1,'23',[4,5]]))#1或者23或者[4,5]
10  
11 print(random.sample([1,'23',[4,5]],2))#列表元素任意2个组合
12  
13 print(random.uniform(1,3))#大于1小于3的小数,如1.927109612082716 
14  
15  
16 item=[1,3,5,7,9]
17 random.shuffle(item) #打乱item的顺序,相当于"洗牌" ,应用:随机验证码
18 print(item)
random模块
# 随机验证码实例
import random
res=''
for i in range(6):
    # 获取随机字母或数字
    # 随机字符=random.choice([从26个大写英文字母中随机取出一个,从10个数字中随机取出一个])
    # 从26个大写英文字母中随机取出一个=chr(random.randint(65,90))
    # 从10个数字中随机取出一个=random.rangdint(0,9),得到的是一个整型 str转换成字符串str(random.rangdint(0,9))
    # res += 随机字符
    # 实现如下:
    s1 = chr(random.randint(65,90))
    s2 = str(random.randint(0,9))
    res += random.choice([s1,s2])
print(res)


# 随机验证码实例
import random
res=''
for i in range(6):
    s1 = chr(random.randint(65,90))
    s2 = str(random.randint(0,9))
    res += random.choice([s1,s2])
print(res)

# 思考:有的验证码是4位,有的验证码是6位,所以参数位置不能写死,修改如下:
def make_code(size=4):
    res = ''
    for i in range(size):
        s1 = chr(random.randint(65, 90))
        s2 = str(random.randint(0, 9))
        res += random.choice([s1, s2])
    print(res)
print(make_code()) # 不传参就是默认4位
# print(make_code(6)) # 传个6位的参数,就是6位


# 查看ASCII表中对应的数值
print(chr(65))  # A
print(chr(90))  # Z
print(ord('A')) # 65
五、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下为"	
",Linux下为"
"
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的大小
OS模块功能
import os
# 查看某一个文件夹下有哪些子文件
# res = os.listdir(r'D:pycharmoldboy_29day022	est022')
# print(res)
# 查看当前文件夹下所有的子文件以及子文件夹的名字

# res = os.listdir('.')
# print(res)
# 需求:统计某一个文件夹大小的功能,如果查看的是文件统计大小,如果是文件夹需要再次打开

# 获取文件大小 单位字节
# size = os.path.getsize(r'D:pycharmoldboy_29day022	est022')
# print(size)

# 运行一条系统命令,
# dir:windows下查看
# os.system("dir C:\a")
# 应用程序--->给操作系统发一条命令ls
# os.system('dir /')

# environ环境变量
# PATH = 文件夹
# sys.path  # 导入模块

# 获取环境变量
# print(os.environ)  # 运行后都是字典形式:{'ALLUSERSPROFILE': 'C:\ProgramData'
# key与value必须都为字符串
# 加入一组key:value,我们后期在系统内可以用到的环境变量
# os.environ['aaaaaaaaaaaaaaaaaaaaaa'] = '1111111111111111111111'
# print(os.environ) # 运行如下:...'AAAAAAAAAAAAAAAAAAAAAA': '1111111111111111111111'
os实例
import os
# print(__file__)  # 查看文件所在位置:D:/pycharm/oldboy_29/day022/test022/test022_09ospath.py
# print(os.path.abspath(__file__))  # 根据平台显示对应的路径分割符格式:D:pycharmoldboy_29day022	est022	est022_09ospath.py

# res = os.path.split('/a/b/c/d.txt')
# print(res)  # 以元组形式划分文件夹名称和文件名称('/a/b/c', 'd.txt')

# 获取文件夹名称
# print(os.path.dirname(r'/a/b/c/d.txt'))  # /a/b/c
# 获取文件名称
# print(os.path.basename(r'/a/b/c/d.txt'))  # d.txt

# 判断是否是绝对路径
# print(os.path.isabs('/a/b/c')) # 是返回True
# print(os.path.isabs('D:Python38python.exe')) # 是返回True

# 判断存在的文件
# print(os.path.isfile(r'test022_09ospath.py'))  # True
# print(os.path.isfile(r'test022_09ospath1.py'))  # False
# print(os.path.isfile(r'test022'))  # 这是个文件夹所以返回False

# 判断是否是文件夹
print(os.path.isdir(r'day022'))  # True

# 将多个路径组合后返回
print(os.path.join('a','/','b'))
在Linux和Mac平台上,该函数会原样返回path,在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠。
>>> os.path.normcase('c:/windows\system32\')   
'c:\windows\system32\'   
   

规范化路径,如..和/ 一个..就向上跳一级
>>> os.path.normpath('c://windows\System32\../Temp/')   
'c:\windows\Temp'   

>>> a='/Users/jieli/test1/\a1/\\aa.py/../..'
>>> print(os.path.normpath(a))
/Users/jieli/test1

# 在python3.5后,退出一个新的模块pathlib
from pathlib import Path
root = Path(__file__)
res = root.parent.parent
print(res)

# D:pycharmoldboy_29day022
 

 六、sys模块

1 sys.argv           命令行参数List,第一个元素是程序本身路径
2 sys.exit(n)        退出程序,正常退出时exit(0)
3 sys.version        获取Python解释程序的版本信息
4 sys.maxint         最大的Int值
5 sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
6 sys.platform       返回操作系统平台名称
sys模块
import sys
# sys.argv获取的是解释器后参数值
print(sys.argv)
# 在交换模式下 python test022_11.py 1 2 3
# D:pycharmoldboy_29day022	est022>python test022_11.py 1 2 3
# ['test022_11.py', '1', '2', '3']
#=========知识储备==========
#进度条的效果
# [#             ]
# [##            ]
# [###           ]
# [####          ]

#指定宽度
# print('[%-15s]' %'#')
# print('[%-15s]' %'##')
# print('[%-15s]' %'###')
# print('[%-15s]' %'####')

import time
# res = ''
# for i in range(50):
#     res+="#"
#     time.sleep(0.3)
#     print('
[%-50s]' % res,end='')

import time

def progress(percent):
    # 进度条的功能
    if percent > 1:
        percent = 1
    res = int(50*percent) * '#'
    print('
[%-50s] %d%%' % (res,int(percent*100)), end='')


recv_size = 0
total_size = 33333333333

while recv_size < total_size:
    time.sleep(0.001)  # 下载了1024个字节的数据
    recv_size += 1024
    # 打印进度条

    # print(recv_size)
    percent = recv_size / total_size  # 1024 / 3333333
    progress(percent)
七、shutil模块

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

shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中

1 import shutil
2  
3 shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))
shutil.copyfile(src, dst)
拷贝文件

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

shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变

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

shutil.copystat(src, dst)
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags

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

shutil.copy(src, dst)
拷贝文件和权限

1 import shutil
2  
3 shutil.copy('f1.log', 'f2.log')
 
shutil.copy2(src, dst)
拷贝文件和状态信息

1 import shutil
2  
3 shutil.copy2('f1.log', 'f2.log')
 
shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件夹

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

shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))

'''
通常的拷贝都把软连接拷贝成硬链接,即对待软连接来说,创建新的文件
'''

拷贝软连接
shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件

1 import shutil
2  
3 shutil.rmtree('folder1')

shutil.move(src, dst)
递归的去移动文件,它类似mv命令,其实就是重命名。

1 import shutil
2  
3 shutil.move('folder1', 'folder3')
 

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

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

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

base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
如 data_bak                       =>保存至当前路径
如:/tmp/data_bak =>保存至/tmp/
format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
root_dir: 要压缩的文件夹路径(默认当前目录)
owner: 用户,默认当前用户
group: 组,默认当前组
logger: 用于记录日志,通常是logging.Logger对象
复制代码
1 #将 /data 下的文件打包放置当前程序目录
2 import shutil
3 ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')
4   
5   
6 #将 /data下的文件打包放置 /tmp/目录
7 import shutil
8 ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data') 
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()

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()

 八、json&pickle模块

  之前我们学过用eval内置方法,可以将一个字符串转成python对象,不过,eval方法是有局限性的,对于普通的数据类型,json.loads和eval都能用,但遇到特殊类型的时候,eval就不管用了,所以eval重点还是通常用来执行一个字符串表达式,并返回表达式的值

# 1、什么是序列化&反序列化?
#   序列化指的是把内存的数据类型转换成一种特定的格式的内容,
#
#   内存中的数据类型-->序列化--> 特定的格式(json格式或者pickle格式)
#   内存中的数据类型<--反序列化<--特定的格式(json格式或者pickle格式)
#
#   土办法:
#   {'a':1} -->序列化str({'a':1})-->"{{'a':1}}"
#   {'a':1} <--反序列化eval({'a':1})<--"{{'a':1}}"
#
# 2、为何要序列化?
#   序列化得到的结果-->特定的格式的内容有两种用途。
#   1、可用于存储(变量的值可以记录某种状态)-->用于存档
#   2、传输给其他平台使用-->跨平台数据交互
#
# 例如:同一个软件内有不同的组件,不同的组件使用不用的语言实现。如何进行数据交互。
#         Python的一个列表给了Java,到java中就是数组,在Python和java之间找个第三方中间格式
#         Python               java
#          列表    特殊格式    数组
# 强调:
#   针对用途1的特定格式:可以是一种专用的格式-->pickle只有python可以识别
#   针对用途2的特定格式:应该是一种通用、能够被所有语言识别的格式==>json
#

如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。

JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下

 

 

# 3、如何使用序列化和反序列化
import json
# 示范一
# 序列化
# json_res = json.dumps(True)
# print(res,type(json_res))  # true <class 'str'>

# json_res = json.dumps([1,'a',True,False])
# print(res,type(json_res))  # 得到json格式:[1, "a", true, false] <class 'str'>

# 反序列化
# l = json.loads(json_res)
# print(l,type(l))  # 得到python格式的:[1, 'a', True, False] <class 'list'>


# 示范二:
# 序列化的结果写入文件的复杂方法,dumps
# json_res = json.dumps([1,'a',True,False])
# with open('test.json',mode='wt',encoding='utf-8') as f:
#     f.write(json_res)
# # 运行结果:[1, "a", true, false]

# 将序列化的结果写入文件的简单方法:dump,一步到位
# with open('test.json',mode='wt',encoding='utf-8') as f:
#     json.dump([1,'a',True,False],f)
# 运行结果:[1, "a", true, false]

# 反序列化:从文件读取json格式的字符串进行序列化操作的复杂方法。loads
# with open('test.json',mode='rt',encoding='utf-8') as f:
#     json_res = f.read()
#     l = json.loads(json_res)
#     print(l,type(l))
# 运行结果:[1, 'a', True, False] <class 'list'>

# 反序列化:从文件读取json格式的字符串进行序列化操作的简单方法。load
# with open('test.json',mode='rt',encoding='utf-8') as f: 
#     l = json.load(f)
#     print(l,type(l))
# 运行结果:[1, 'a', True, False] <class 'list'>
# json补充
# json验证:json格式兼容的是所有语言通用的数据类型,不能识别某一语言所独有的类型
import json
# json.dumps({1,2,3,4,5})

# json强调:json.loads('[1, "a", true, false]')格式要放json格式的内容
# l = json.loads('[1, "a", true, false]')
# print(l[0])
# 1
# json格式不能于python混淆,python格式里的True
# l = json.loads('[1, "a", True, False]')
# print(l[0])
# json与python格式混淆后报错,json.decoder.JSONDecodeError: Expecting value: line 1 column 10 (char 9)

# python3.5以前的版本要加入b
# l = json.loads(b'[1, "a", true, false]')
# print(l[0])

# with open('test.json',mode='rt') as f:
#     l = json.load(f)
#
# res = json.dumps({'name':'hahaha哈哈哈'})
# print(res,type(res))  # {"name": "hahahau54c8u54c8u54c8"} <class 'str'>

# res = json.loads('{"name": "hahahau54c8u54c8u54c8"}')
# print(res,type(res))  # {'name': 'hahaha哈哈哈'} <class 'dict'>

 九、猴子补丁

# 一.什么是猴子补丁?
      猴子补丁的核心就是用自己的代码替换所用模块的源代码,详细地如下
  1,这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。
  2,还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。


# 二. 猴子补丁的功能(一切皆对象)
  1.拥有在模块运行时替换的功能, 例如: 一个函数对象赋值给另外一个函数对象(把函数原本的执行的功能给替换了)
class Monkey:
    def hello(self):
        print('hello')

    def world(self):
        print('world')


def other_func():
    print("from other_func")



monkey = Monkey()
monkey.hello = monkey.world
monkey.hello()
monkey.world = other_func
monkey.world()

# 三.monkey patch的应用场景
如果我们的程序中已经基于json模块编写了大量代码了,发现有一个模块ujson比它性能更高,
但用法一样,我们肯定不会想所有的代码都换成ujson.dumps或者ujson.loads,那我们可能
会想到这么做
import ujson as json,但是这么做的需要每个文件都重新导入一下,维护成本依然很高
此时我们就可以用到猴子补丁了
只需要在入口处加上
, 只需要在入口加上:

import json
import ujson

def monkey_patch_json():
    json.__name__ = 'ujson'
    json.dumps = ujson.dumps
    json.loads = ujson.loads

monkey_patch_json() # 之所以在入口处加,是因为模块在导入一次后,后续的导入便直接引用第一次的成果

#其实这种场景也比较多, 比如我们引用团队通用库里的一个模块, 又想丰富模块的功能, 除了继承之外也可以考虑用Monkey
Patch.采用猴子补丁之后,如果发现ujson不符合预期,那也可以快速撤掉补丁。个人感觉Monkey
Patch带了便利的同时也有搞乱源代码的风险!

猴子补丁与ujson

十、pickle模块

import pickle
 
dic={'name':'alvin','age':23,'sex':'male'}
 
print(type(dic))#<class 'dict'>
 
j=pickle.dumps(dic)
print(type(j))#<class 'bytes'>
 
 
f=open('序列化对象_pickle','wb')#注意是w是写入str,wb是写入bytes,j是'bytes'
f.write(j)  #-------------------等价于pickle.dump(dic,f)
 
f.close()
#-------------------------反序列化
import pickle
f=open('序列化对象_pickle','rb')
 
data=pickle.loads(f.read())#  等价于data=pickle.load(f)
 
 
print(data['age'])
# coding:utf-8
import pickle

with open('a.pkl',mode='wb') as f:
    # 一:在python3中执行的序列化操作如何兼容python2
    # python2不支持protocol>2,默认python3中protocol=4
    # 所以在python3中dump操作应该指定protocol=2
    pickle.dump('你好啊',f,protocol=2)

with open('a.pkl', mode='rb') as f:
    # 二:python2中反序列化才能正常使用
    res=pickle.load(f)
    print(res)

python2与python3的pickle兼容性问题

Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。  

十一、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>

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) #打印生成的格式

 十二、configparser模块,加载某种特定格式的配置文件

配置文件如下:数据准备test.ini,类似于settings.py的样式 
# 注释1
; 注释2

[section1]
k1 = v1
k2:v2
user=egon
age=18
is_admin=true
salary=31

[section2]
k1 = v1
import configparser

config=configparser.ConfigParser()
config.read('a.cfg')

#查看所有的标题
res=config.sections() #['section1', 'section2']
print(res)

#查看标题section1下所有key=value的key
options=config.options('section1')
print(options) #['k1', 'k2', 'user', 'age', 'is_admin', 'salary']

#查看标题section1下所有key=value的(key,value)格式
item_list=config.items('section1')
print(item_list) #[('k1', 'v1'), ('k2', 'v2'), ('user', 'egon'), ('age', '18'), ('is_admin', 'true'), ('salary', '31')]

#查看标题section1下user的值=>字符串格式
val=config.get('section1','user')
print(val) #egon

#查看标题section1下age的值=>整数格式
val1=config.getint('section1','age')
print(val1) #18

#查看标题section1下is_admin的值=>布尔值格式
val2=config.getboolean('section1','is_admin')
print(val2) #True

#查看标题section1下salary的值=>浮点型格式
val3=config.getfloat('section1','salary')
print(val3) #31.0
# 修改
import configparser

config=configparser.ConfigParser()
config.read('a.cfg',encoding='utf-8')


#删除整个标题section2
config.remove_section('section2')

#删除标题section1下的某个k1和k2
config.remove_option('section1','k1')
config.remove_option('section1','k2')

#判断是否存在某个标题
print(config.has_section('section1'))

#判断标题section1下是否有user
print(config.has_option('section1',''))


#添加一个标题
config.add_section('egon')

#在标题egon下添加name=egon,age=18的配置
config.set('egon','name','egon')
config.set('egon','age',18) #报错,必须是字符串


#最后将修改的内容写入文件,完成最终的修改
config.write(open('a.cfg','w'))
基于上述方法添加一个ini文档
import configparser
  
config = configparser.ConfigParser()
config["DEFAULT"] = {'ServerAliveInterval': '45',
                      'Compression': 'yes',
                     'CompressionLevel': '9'}
  
config['bitbucket.org'] = {}
config['bitbucket.org']['User'] = 'hg'
config['topsecret.server.com'] = {}
topsecret = config['topsecret.server.com']
topsecret['Host Port'] = '50022'     # mutates the parser
topsecret['ForwardX11'] = 'no'  # same here
config['DEFAULT']['ForwardX11'] = 'yes'
with open('example.ini', 'w') as configfile:
   config.write(configfile)

 十三、hash介绍

# 1、什么叫hash:
  hash是一种算法(3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法),
    该算法接受传入的内容,经过运算得到一串hash值
# 2、hash值的特点是: #2.1 只要传入的内容一样,得到的hash值必然一样=====>要用明文传输密码文件完整性校验 #2.2 不能由hash值返解成内容=======》把密码做成hash值,不应该在网络传输明文密码 #2.3 只要使用的hash算法不变,无论校验的内容有多大,得到的hash值长度是固定的

  hash算法就像一座工厂,工厂接收你送来的原材料(可以用m.update()为工厂运送原材料),经过加工返回的产品就是hash值

import hashlib
 
m=hashlib.md5()# m=hashlib.sha256()
 
m.update('hello'.encode('utf8'))
print(m.hexdigest())  #5d41402abc4b2a76b9719d911017c592
 
m.update('alvin'.encode('utf8'))
 
print(m.hexdigest())  #92a7e713c30abbb0319fa07da2a5c4af
 
m2=hashlib.md5()
m2.update('helloalvin'.encode('utf8'))
print(m2.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af

'''
注意:把一段很长的数据update多次,与一次update这段长数据,得到的结果一样
但是update多次为校验大文件提供了可能。
'''
# 如何使用hash
import hashlib
m = hashlib.md5() # fc5e038d38a57032085441e7fe7010b0
# m = hashlib.sha256()  # 936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af
m.update('hello'.encode('utf-8'))
m.update('world'.encode('utf-8'))
res = m.hexdigest() # hello world
print(res)  # 只要传入的内容一样,得到的hash值必然一样:fc5e038d38a57032085441e7fe7010b0

m1 = hashlib.md5('he'.encode('utf-8'))
m1.update('llo'.encode('utf-8'))
m1.update('world'.encode('utf-8'))
res = m1.hexdigest()
print(res)  # 只要传入的内容一样,得到的hash值必然一样:fc5e038d38a57032085441e7fe7010b0 

以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。

import hashlib
 
# ######## 256 ########
 
hash = hashlib.sha256('898oaFs09f'.encode('utf8'))
hash.update('alvin'.encode('utf8'))
print (hash.hexdigest())#e79e68f070cdedcfe63eaf1a2e92c83b4cfb1b5c6bc452d214c1b7e77cdfd1c7
# 撞库的实例:

#模拟撞库:
# 截获的密文如下
cryptograph = 'aee949757a2e698417463d47acac93df'
# 猜测的密码如下
passwds=[
    'alex3714',
    'alex1313',
    'alex94139413',
    'alex123456',
    '123456alex',
    'a123lex',
    ]
# 做一个密码字典如下
import hashlib
dic = {}
for p in passwds:
    res = hashlib.md5(p.encode('utf-8'))
    dic[p] = res.hexdigest()
print(dic)

# 进行比对
for k,v in dic.items():
    if v == cryptograph:
        print('撞库成功,明文密码是:%s',k)
# 提示撞库的成本==>密码加盐,拆分密码前后加入混淆数据
import hashlib
m = hashlib.md5()
m.update('天王'.encode('utf-8'))
m.update('alex3714'.encode('utf-8'))
m.update('盖地虎'.encode('utf-8'))
print(m.hexdigest())  # ef606e983c958a13273eaad0b29a054c
"""
密码加盐:
'a'天l王e盖x地3虎714
"""
# 密码加盐的优点:
# 攻击思路:撞库密码

# md5可以反解

 十四、suprocess模块

import subprocess
# 执行系统命令
obj = subprocess.Popen('ls /root',shell=True,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
                 )
print(obj)  # <subprocess.Popen object at 0x000001FC3FCF6B80>
res = obj.stdout.read()  # 正确的结果
print(res.decode('gbk'),type(res))  # b'' <class 'bytes'>
# err_res = obj.stderr.read() # 错误结果
# print(err_res.decode('gbk'),type(err_res))  # windows系统使用gbk
# print(err_res.decode('utf-8'),type(err_res))  # Linux系统使用utf-8

 十五、logging模块

一 日志级别

CRITICAL = 50 #FATAL = CRITICAL
ERROR = 40
WARNING = 30 #WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0 #不设置
自下而上输出

 

import logging

# 一:日志配置
logging.basicConfig(
    # 1、日志输出位置:1、终端 2、文件
    # filename='access.log', # 不指定,默认打印到终端

    # 2、日志格式
    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',

    # 3、时间格式
    datefmt='%Y-%m-%d %H:%M:%S %p',

    # 4、日志级别
    # critical => 50
    # error => 40
    # warning => 30
    # info => 20
    # debug => 10
    level=30,
)

# 二:输出日志
logging.debug('调试debug,工程师调试用想出现的信息')
logging.info('消息info,正常信息')
logging.warning('警告warn,可以这样但要注意了')
logging.error('错误error,程序出现了错误')
logging.critical('严重critical,可能导致程序崩溃')
''' 运行结果如下,默认级别为warning,默认打印到终端
# 注意下面的root是默认的日志名字 
WARNING:root:警告warn,可以这样但要注意了
ERROR:root:错误error,程序出现了错误
CRITICAL:root:严重critical,可能导致程序崩溃
'''
2、日志配置字典

"""
logging配置
"""

import os

# 1、定义三种日志输出格式,日志中可能用到的格式化串如下
# %(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用户输出的消息

# 2、强调:其中的%(name)s为getlogger时指定的名字
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'

test_format = '%(asctime)s] %(message)s'

# 3、日志配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
        'test': {
            'format': test_format
        },
    },
    'filters': {},
    'handlers': {
        #打印到终端的日志
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,日志轮转
            'formatter': 'standard',
            # 可以定制日志文件路径
            # BASE_DIR = os.path.dirname(os.path.abspath(__file__))  # log文件的目录
            # LOG_PATH = os.path.join(BASE_DIR,'a1.log')
            'filename': 'a1.log',  # 日志文件
            'maxBytes': 1024*1024*5,  # 日志大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
        'other': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',  # 保存到文件
            'formatter': 'test',
            'filename': 'a2.log',
            'encoding': 'utf-8',
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
            'propagate': False,  # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
        },
        '专门的采集': {
            'handlers': ['other',],
            'level': 'DEBUG',
            'propagate': False,
        },
    },
}
# encoding=utf-8
# auther:lsj
"""
logging配置:日志配置字典LOGGING_DIC
"""

import os

# 1、定义三种日志输出格式,日志中可能用到的格式化串如下
# %(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用户输出的消息

# 2、强调:其中的%(name)s为getlogger时指定的名字,定义格式如下三种
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'

test_format = '%(asctime)s] %(message)s'

# 3、日志配置字典
LOGGING_DIC = {
    'version': 1,# 版本
    'disable_existing_loggers': False, # 关闭已存在的日志
    'formatters': {  # 记住 formatters名不可改
        'standard': {  # standard自定义个名字可以改,标准格式
            'format': standard_format  # format名字不可以改
        },
        'simple': { # simple 自定义名字可以修改,简单格式
            'format': simple_format  # format名字不可以改
        },
        'test': {  # 自定义名字可以修改,最简单的格式
            'format': test_format  # format名字不可以改
        },
    },# 格式化
    'filters': {},
    # handlers是日志的接收者,不同的handler会将日志输出给不同的位置
    'handlers': {
        #打印到终端的日志
        'console': {  # console该key是可以修改的
            'level': 'DEBUG',  # 日志级别,level名字不能改
            'class': 'logging.StreamHandler',  # 打印到屏幕,key名字不能改
            'formatter': 'simple' # key名字不能改
        },
        #打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,日志轮转
            'formatter': 'standard',
            # 可以定制日志文件路径
            # BASE_DIR = os.path.dirname(os.path.abspath(__file__))  # log文件的目录
            # LOG_PATH = os.path.join(BASE_DIR,'a1.log')
            'filename': 'a1.log',  # 日志文件
            'maxBytes': 1024*1024*5,  # 日志大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
        # 打印到其他位置
        'other': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',  # 保存到文件
            'formatter': 'test',
            'filename': 'a2.log',  # 文件路径,在项目中这里路径os.path.json(os.path.dirname(os.path.dirname(__file__)),'log','a2.log')
            'encoding': 'utf-8',
        },
    },# 记住
    # loggers日志的产生者,产生的日志会传递给handler,然后控制输出
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        '第一个日志的产生者:kkk': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
            'propagate': False,  # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
        },
        '第二个日志的产生者:bbb': {
            'handlers': ['other',],
            'level': 'DEBUG',
            'propagate': False,
        },
    },  # 记住
}
test024_05settings.py
# encoding=utf-8
# auther:lsj

# 接下来要做到的是:拿到日志的产生者
# 第一个日志的产生者:kkk
# 第二个日志的产生者:bbb
# 如何拿到kkk和bbb?

# (1)但是需要先导入日志配置字典LOGGING_DIC
import test025_05settings
import logging

# 以下的两种导入config意义相同
# import logging.config  # 这种导入在使用的时候注意就得用logging.config
# from logging import config

from logging import config,getLogger

# 查看config在哪?
# import logging
# logging.config  # AttributeError: module 'logging' has no attribute 'config'

# (2)加载配置
config.dictConfig(test025_05settings.LOGGING_DIC)
# 使用import logging.config导入方式使用如下
# logging.config.dictConfig(test025_05settings.LOGGING_DIC)
# (3)获取日志字典里的某一个方法
logger1 = getLogger('第一个日志的产生者:kkk')
# settings.LOGGING_DIC

# (4)产生日志,此时在终端和文件都产生了日志
logger1.info('这是一条info日志')
# 终端日志:[INFO][2020-04-17 15:12:49,191][test024_05src.py:32]这是一条info日志
# a1.log文件日志:[2020-04-17 15:12:49,191][MainThread:11492][task_id:第一个日志的产生者:kkk][test024_05src.py:32][INFO][这是一条info日志]

# 如果我只想往终端输出日志使用
logger2 = getLogger('第二个日志的产生者:bbb')
logger2.info('logger2只向终端输出一条info日志')
# 终端日志输出:[INFO][2020-04-17 15:19:11,047][test024_05src.py:32]这是一条info日志
test024_05src.py

# 日志名的命名
# 日志名是区分日志业务归属的一种非常重要的标识
# settings 里设置默认的日志名字
         '': {  # 如果用户找不大就默认使用它默认
            'handlers': ['default', ],
            'level': 'DEBUG',
            'propagate': False,
        },  

# 在src文件中调用一个不存在的名字
logger4 = getLogger('fsa')
logger4.info("找不到名字就使用空的默认")

 十六、re正则模块

1、什么是正则?

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

2、生活中处处都是正则

  比武我们描述:4条腿。你可能 会想到四条腿的动物或者桌子,椅子等,继续描述:4条腿,活得,就只剩下四条腿的动物这一类了。

3、常用匹配模式(元字节)

# encoding=utf-8
# auther:lsj

# 正则re实例
import re
print(re.findall('w','abc123*&^_= '))  # w:字母数字下划线。运行结果:['a', 'b', 'c', '1', '2', '3', '_']
print(re.findall('W','abc123*&^_= '))  # W:非字母数字下划线。运行结果:['*', '&', '^', '=', ' ']
print(re.findall('s','abc	

f123*&^_= '))  # s:匹配任意空白字符。运行结果:['	', '
', '
', 'x0c', ' ']
print(re.findall('S','abc	

f123*&^_= '))  # s:非空白字符。运行结果:['a', 'b', 'c', '1', '2', '3', '*', '&', '^', '_', '=']
print(re.findall('d','abcASD123*&^_= '))  # d:匹配任意数字。运行结果:['1', '2', '3']
print(re.findall('D','abcASD123*&^_= '))  # D:匹配任意非数字。运行结果:['a', 'b', 'c', 'A', 'S', 'D', '*', '&', '^', '_', '=', ' ']
print(re.findall('Aabc','abcASD123abc*&^_= '))  # A:匹配字符串开头。运行结果:['abc']
print(re.findall('Aabc',' abcASD123abc*&^_= '))  # A:匹配字符串开头。运行结果:[]
print(re.findall('abc',' abcASD123abcABC*&^_= '))  # 查找所有匹配的字符,运行结果:['abc', 'abc']
print(re.findall('a',' abcASD123abcABC*&^_= '))  # 匹配字符串结束,运行结果:[]
print(re.findall('a',' abcASD123abcABC*&^_= a'))  # 匹配字符串结束,运行结果:['a']
print(re.findall('a',"""
abc
ASD
123
abc
ABC*&^_= 
a"""))  # 匹配到换行前的字符串结束,运行结果:['a']

print(re.findall('^a',' abcASD123abcABC*&^_= a'))  # 匹配字符串开头,运行结果:[]
print(re.findall('a$',' abcASD123abcABC*&^_= a'))  # 匹配字符串结尾,运行结果:['a']
print(re.findall('^ab$',' abcASD123abcABC*&^_= a'))  # 同时匹配字符串的开头和结尾,运行结果:[]
print(re.findall('^ab$','ab'))  # 同时匹配字符串的开头和结尾,运行结果:['ab']
print(re.findall('^ab$','a b'))  # 同时匹配字符串的开头和结尾,运行结果:[]

# 重复匹配:|.|*|?|.*|.*?|+|{n,m}|
print(re.findall('.','abc
123*&^_= ')) # .:匹配除了
之外任意一个字符,运行结果:['a', 'b', 'c', '1', '2', '3', '*', '&', '^', '_', '=', ' ']
print(re.findall('a.b','a1b a
b a|b a	b'))  # .:匹配除了
之外任意一个字符,运行结果:['a1b', 'a|b', 'a	b']
print(re.findall('a.b','a1b a
b a|b a	b',re.DOTALL))  # .:匹配除了
之外任意一个字符,使用:re.DOTALL之后才能匹配换行符,运行结果:['a1b', 'a
b', 'a|b', 'a	b']

print(re.findall('..','abc
123*&^_= ')) # .:匹配除了
之外任意两个字符,运行结果:['ab', '12', '3*', '&^', '_=']

# 2、* 左侧字符重复0此或无穷次,性格贪婪
# ab*:取得b可以是0到无穷,a是必须存在
print(re.findall('ab*','abc
123*&^_= absd a abb abbbbbb abbhb bbbbb'))  # 运行结果:['ab', 'ab', 'a',abb', 'abbbbbb', 'abb']

# 3、+:左侧字符重复1次或无穷次,性格贪婪
# a必须存在,b至少出现1此
print(re.findall('ab+','ab abb abbb abbbbb')) # 运行结果: ['ab', 'abb', 'abbb', 'abbbbb']

# 4、?:左侧字符重复0次或1次,性格贪婪
print(re.findall('ab?','a ab abb abbb abbbbb')) # 运行结果:['a', 'ab', 'ab', 'ab', 'ab']

# 5、{n,m},自定义左侧字符重复n次到m次,性格贪婪
# {0,} = *  0到无穷
# {1,} = +  1到无穷
# {0,1} = ? 0到1次
# {n} 单独一个n代表只出现n次,多一次不行少一次也不行。
# ab{2,5} = a必须存在,b至少存在2次到5次
print(re.findall('ab{2,5}','a ab abb abbb abbbbb')) # 运行结果:['abb', 'abbb', 'abbbbb']

# 练习:找出下列字符中所有的整数和小数
# .的体现.
print(re.findall("d+.?d*","ahfu9qwn22256.fjar7890t1.215gdgr54"))

# 6、[]匹配指定字符一个
# 如下字符串,找到ab之间存在1个数字的字符
print(re.findall('[a]d{1}[b]','a|b a3b a26b a2548b aXb a b a
b')) # ['a3b']
print(re.findall('[a]d[b]','a|b a3b a26b a2548b aXb a b a
b')) # ['a3b']

# 如下字符串,找到ab之间存在0-5个数字的字符
print(re.findall('[a]d{0,5}[b]','a|b a3b a26b a2548b aXb a b a
b')) # ['a3b', 'a26b', 'a2548b']

# 如下字符串,找到ab之间存在的是0-5的数字的字符
print(re.findall('a[501234][b]','a|b a3b a26b a2548b aXb a b a
b')) # ['a3b']
print(re.findall('a[0-5][b]','a|b a3b a26b a2548b aXb a b a
b')) # ['a3b']


# 如下字符串,找到ab之间存在的是0-9的数字或者a-z的字母的字符
print(re.findall('a[0-9a-zA-Z]b','a|b a3b a26b a2548b aXb a b a
b')) # ['a3b', 'aXb']

# 如下字符串,找到ab之间存在的是0-9的数字或者a-z的字母的字符,
# []内有^代表取反
# ^代表以匹配的字符开头。
print(re.findall('a[^0-9a-zA-Z]b','a|b a3b a26b a2548b aXb a b a
b',re.DOTALL)) # ['a|b', 'a b', 'a
b']


# 如下字符串,找到a-b的字符,
print(re.findall('a-b',' a-b a|b a3b a26b a2548b aXb a b a
b',re.DOTALL)) # ['a-b']

# 找出a-b 以及ab之间存在一个数字的字符
# -在[]内最左侧或者最右侧代表普通字符
print(re.findall('a[-0-9-
]b',' a-b a|b a3b a26b a2548b aXb a b a
b',re.DOTALL)) # ['a-b', 'a3b', 'a
b']

 

 

原文地址:https://www.cnblogs.com/liunaixu/p/12703170.html