Python常用内置模块之re、collections、time和datetime
一、re模块
在Python中要想使用正则必须借助于模块,re就是其中之一。
1.1 re模块下的常用方法
-
findall()
返回所有满足匹配条件的结果,放在列表里:
import re ret = re.findall('a', 'eva jason yuan') print(ret) # 结果 : ['a', 'a', 'a']
-
search()
根据正则表达式匹配到一个符合条件的就结束:
import re ret = re.search('a', 'eva jason yuan') print(ret) # 结果对象 print(ret.group()) # 真正的结果:'a' """如果没有符合条件的数据 那么search返回None 并且使用group会直接报错"""
-
match()
根据正则表达式从头开始匹配(文本内容必须在开头匹配上):
import re ret = re.match('a', 'abac') print(ret) # 结果对象 print(ret.group()) # 真正的结果:'a' """如果没有符合条件的数据 那么match返回None 并且使用group会直接报错"""
1.2 re模块其他方法
-
split()
import re ret = re.split('[ab]', 'abcd') """先按'a'分割得到''和'bcd',再对''和'bcd'分别按'b'分割""" print(ret) # ['', '', 'cd']
-
sub()
类似于字符串类型的replace方法:
import re ret = re.sub('\d', 'H', 'eva3jason4yuan7', 1) """将数字替换成'H',参数1表示只替换1个""" print(ret) # evaHjason4yuan7
-
subn()
import re ret = re.subn('\d', 'H', 'eva3jason4yuan7') """将数字替换成'H',返回元组(替换的结果,替换了多少次)""" print(ret) # ('evaHjasonHyuanH', 3)
-
compile()
import re obj = re.compile('\d{3}') # 将正则表达式编译为一个正则表达式对象,规则要匹配的是3个数字 ret = obj.search('abc123eeee') # 正则表达式对象调用search,参数为待匹配的字符串 print(ret.group()) # 结果: 123
-
finditer()
import re ret = re.finditer('\d', 'ds3sy4784a') # finditer返回一个存放匹配结果的迭代器 print(ret) # 迭代器对象 print(next(ret).group()) # 查看第一个结果: 3 print(next(ret).group()) # 查看第二个结果: 4 print([i.group() for i in ret]) # 查看剩余的左右结果: ['7', '8', '4']
1.3 无名分组和有名分组
-
无名分组:
import re res = re.search('^[1-9](\d{14})(\d{2}[0-9x])?$', '110105199812067023') print(res) # 结果对象 print(res.group()) # 默认拿的是整体匹配结果(group(0)): 110105199812067023 print(res.group(1)) # 从左到右第一个分组匹配结果: 10105199812067 print(res.group(2)) # 从左到右第二个分组匹配结果: 023
-
有名分组:
import re res = re.search('^[1-9](?P
\d{14})(?P \d{2}[0-9x])?$', '500110199812052832') print(res) # 结果对象 print(res.group()) # 默认取出整体匹配结果: 500110199812052832 print(res.group(1)) # 无名分组的取值方式(索引取): 00110199812052 print(res.group('xxx')) # 取出名字为xxx的分组匹配结果: 00110199812052 print(res.group('ooo')) # 取出名字为ooo的分组匹配结果: 832
1.4 findall与split的优先级查询
-
findall的优先级查询:
import re ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['oldboy']
这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可,如下:
ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['www.oldboy.com']
-
split的优先级查询:
import re ret = re.split("\d+", "eva3jason4yuan") print(ret) # 结果: ['eva', 'jason', 'yuan'] ret = re.split("(\d+)", "eva3jason4yuan") print(ret) # 结果: ['eva', '3', 'jason', '4', 'yuan']
在匹配部分加上()之后所切出的结果是不同的,没有()的没有保留所匹配的项,但是有()的却能够保留匹配的项,这个在某些需要保留匹配部分的使用场景中是非常重要的。
二、collections模块
在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。
- namedtuple: 生成可以使用名字来访问元素内容的tuple
- deque: 双端队列,可以快速的从另外一侧追加和推出对象
- Counter: 计数器,主要用来计数
- OrderedDict: 有序字典
- defaultdict: 带有默认值的字典
2.1 namedtuple(具名元组)
我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:
p = (1, 2)
但是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。
这时,namedtuple就派上了用场:
from collections import namedtuple
point = namedtuple('坐标', ['x', 'y'])
res = point(11, 22)
print(res) # 坐标(x=11, y=22)
print(res.x) # 11
print(res.y) # 22
用具名元组来记录一个城市的信息:
>>> from collections import namedtuple
>>> City = namedtuple('City', 'name country population coordinates')
"""第一个是类名,第二个是类的各个字段的名字。后者可以是由数个字符串组成的可迭代对象,或者是由空格分隔开的字段名组成的字符串"""
>>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
>>> tokyo
City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
>>> tokyo.population
36.933
>>> tokyo.coordinates
(35.689722, 139.691667)
>>> tokyo[1]
'JP'
类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:
# namedtuple('名称', [属性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])
2.2 deque(双端队列)
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适用于队列和栈:
from collections import deque
q = deque(['a', 'b', 'c'])
q.append('x')
q.appendleft('y')
print(q) # 结果: deque(['y', 'a', 'b', 'c', 'x'])
print(q.pop()) # 从右边取值: x
print(q.popleft()) # 从左边取值: y

2.3 OrderedDict(有序字典)
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。
如果要保持Key的顺序,可以用OrderedDict:
>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> 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的顺序返回
['z', 'y', 'x']
2.4 defaultdict(默认字典)
有如下值集合:[11, 22, 33, 44, 55, 66, 77, 88, 99, 90],将所有大于66的值保存至字典的第一个key中,将小于66的值保存至第二个key的值中。
即:{'k1': 大于66, 'k2': 小于66}
原生字典解决方案:
values = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90]
my_dict = {}
for value in values:
if value > 66:
if my_dict.get('k1'):
my_dict['k1'].append(value)
else:
my_dict['k1'] = [value]
else:
if my_dict.get('k2'):
my_dict['k2'].append(value)
else:
my_dict['k2'] = [value]
defaultdict字典解决方法:
from collections import defaultdict
values = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90]
my_dict = defaultdict(list)
for value in values:
if value > 66:
my_dict['k1'].append(value)
else:
my_dict['k2'].append(value)
使用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'
2.5 Counter(计数器)
Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。Counter类和其他语言的bags或multisets很相似。
from collections import Counter
c = Counter('abcdeabcdabcaba')
print(c) # 输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
其他详细内容

三、time模块
和时间有关系的我们就要用到时间模块。在使用模块之前,应该首先导入这个模块。
常用方法:
- time.sleep(secs):(线程)推迟指定的时间运行,单位为秒;
- time.time():获取当前时间戳;
3.1 表示时间的三种方式
在Python中,通常有这三种方式来表示时间:时间戳、元组(struct_time)、格式化的时间字符串:
-
时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。
-
格式化的时间字符串(Format String):‘2021-11-25’。
Python中时间日期格式化符号:
%y 两位数的年份表示(00-99) %Y 四位数的年份表示(000-9999) %m 月份(01-12) %d 月内中的一天(0-31) %H 24小时制小时数(0-23) %I 12小时制小时数(01-12) %M 分钟数(00=59) %S 秒(00-59) %a 本地简化星期名称 %A 本地完整星期名称 %b 本地简化的月份名称 %B 本地完整的月份名称 %c 本地相应的日期表示和时间表示 %j 年内的一天(001-366) %p 本地A.M.或P.M.的等价符 %U 一年中的星期数(00-53)星期天为星期的开始 %w 星期(0-6),星期天为星期的开始 %W 一年中的星期数(00-53)星期一为星期的开始 %x 本地相应的日期表示 %X 本地相应的时间表示 %Z 当前时区的名称 %% %号本身
-
元组(struct_time) :struct_time元组共有九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)
索引(Index) 属性(Attribute) 值(Values) 0 tm_year(年) 比如2021 1 tm_mon(月) 1 - 12 2 tm_mday(日) 1 - 31 3 tm_hour(时) 0 - 23 4 tm_min(分) 0 - 59 5 tm_sec(秒) 0 - 60 6 tm_wday(weekday) 0 - 6(0表示周一) 7 tm_yday(一年中的第几天) 1 - 366 8 tm_isdst(是否是夏令时) 默认为0
首先,我们先导入time模块,来认识一下Python中表示时间的几种格式:
# 导入时间模块
import time
# 时间戳
print(time.time()) # 1637838914.133354
# 时间字符串
print(time.strftime("%Y-%m-%d %X")) # '2021-11-25 19:15:14'
print(time.strftime("%Y-%m-%d %H-%M-%S")) # '2021-11-25 19-15-14'
# 时间元组:localtime将一个时间戳转换为当前时区的struct_time
print(time.localtime()) # time.struct_time(tm_year=2021, tm_mon=11, tm_mday=25, tm_hour=19, tm_min=15, tm_sec=14, tm_wday=3, tm_yday=329, tm_isdst=0)
小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的。
3.2 几种时间格式的转换
时间戳-->结构化时间:
# time.gmtime(时间戳)——UTC时间,与英国伦敦当地时间一致
# time.localtime(时间戳)——当地时间。例如我们现在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间
>>> time.gmtime(1500000000)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)
>>> time.localtime(1500000000)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)
结构化时间-->时间戳:
# time.mktime(结构化时间)
>>> time_tuple = time.localtime(1500000000)
>>> time.mktime(time_tuple)
1500000000.0
结构化时间-->格式化字符串时间:
# time.strftime("格式定义", "结构化时间") 结构化时间参数若不传,则显示当前时间
>>> time.strftime("%Y-%m-%d %X")
'2021-11-25 19:35:05'
>>> time.strftime("%Y-%m-%d",time.localtime(1500000000))
'2017-07-14'
格式化字符串时间-->结构化时间:
# time.strptime(时间字符串, 字符串对应格式)
>>> time.strptime("2021-11-25","%Y-%m-%d")
time.struct_time(tm_year=2021, tm_mon=11, tm_mday=25, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=329, tm_isdst=-1)
>>> time.strptime("07/24/2017","%m/%d/%Y")
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)
结构化时间 --> %a %b %d %H:%M:%S %Y串:
# time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串
>>> time.asctime(time.localtime(1500000000))
'Fri Jul 14 10:40:00 2017'
>>> time.asctime()
'Thu Nov 25 19:39:51 2021'
时间戳 --> %a %b %d %H:%M:%S %Y串:
# time.ctime(时间戳) 如果不传参数,直接返回当前时间的格式化串
>>> time.ctime()
'Thu Nov 25 19:41:36 2021'
>>> time.ctime(1500000000)
'Fri Jul 14 10:40:00 2017'
案例:计算时间差
import time
true_time = time.mktime(time.strptime('2017-09-11 08:30:00', '%Y-%m-%d %H:%M:%S'))
time_now = time.mktime(time.strptime('2021-11-25 11:00:00', '%Y-%m-%d %H:%M:%S'))
dif_time = time_now - true_time
struct_time = time.gmtime(dif_time)
print('过去了%d年%d月%d天%d小时%d分钟%d秒' % (struct_time.tm_year - 1970, struct_time.tm_mon - 1,
struct_time.tm_mday - 1, struct_time.tm_hour,
struct_time.tm_min, struct_time.tm_sec))
四、datetime模块
4.1 自定义日期
import datetime
res = datetime.date(2021, 11, 25)
print(res) # 2021-11-25
4.2 获取本地时间
import datetime
# 年月日
now_date = datetime.date.today()
print(now_date) # 2021-11-25
# 年月日时分秒
now_time = datetime.datetime.today()
print(now_time) # 2021-11-25 19:56:15.298526
无论是年月日,还是年月日时分秒对象都可以调用以下方法获取针对性的数据:
# 以datetime对象举例
print(now_time.year) # 获取年份2021
print(now_time.month) # 获取月份11
print(now_time.day) # 获取日25
print(now_time.weekday()) # 获取星期(weekday星期是0-6) 0表示周一
print(now_time.isoweekday()) # 获取星期(weekday星期是1-7) 1表示周一

4.3 timedelta对象(可以对时间进行运算操作)
import datetime
# 获得本地日期: 年月日
now_day = datetime.date.today()
# 定义操作时间 days=7 也就是可以对另一个时间对象加7天或者减少7天
time_delta = datetime.timedelta(days=7)
# 打印今天的日期
print('今天的日期:{}'.format(now_day))
# 打印七天后的日期
print('从今天向后推7天:{}'.format(now_day + time_delta))
打印结果为:
今天的日期:2021-11-25
从今天向后推7天:2021-12-02
总结:日期对象与timedelta之间的关系:
日期对象 = 日期对象 +/- timedelta对象
timedelta对象 = 日期对象 +/- 日期对象
验证:
import datetime
# 定义日期对象
now_date1 = datetime.date.today()
# 定义timedelta对象
lta = datetime.timedelta(days=6)
now_date2 = now_date1 + lta # 日期对象 = 日期对象 +/- timedelta对象
print(type(now_date2)) #
lta2 = now_date1 - now_date2 # timedelta对象 = 日期对象 +/- 日期对象
print(type(lta2)) #
4.4 小练习:计算距离今年过生日还有多少天
import datetime
birthday = datetime.date(2021, 12, 5)
now_date = datetime.date.today()
days = birthday - now_date
print('生日:{}'.format(birthday))
print('今天的日期:{}'.format(now_date))
print('距离生日还有{}'.format(days))
4.5 总结年月日时分秒及时区问题
import datetime
dt_today = datetime.datetime.today()
dt_now = datetime.datetime.now()
dt_utcnow = datetime.datetime.utcnow() # UTC时间与我们的东八区时间差为八个小时
print(dt_today)
print(dt_now)
print(dt_utcnow)
打印结果为:
2021-11-25 20:20:31.403982
2021-11-25 20:20:31.403981
2021-11-25 12:20:31.403981
