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会直接报错"""
    
    ???é¥??12??? - ???é¥??12????|1????±??¥?è·ˉ_????|1????±?è?¨???

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']
    

    ?????? - ???é¥??12????|1????±??¥?è·ˉ_????|1????±?è?¨???

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的优先级查询

  1. 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']
    
  2. 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']
    

    在匹配部分加上()之后所切出的结果是不同的,没有()的没有保留所匹配的项,但是有()的却能够保留匹配的项,这个在某些需要保留匹配部分的使用场景中是非常重要的。

    ??3??????? - ???é¥??12????|1????±??¥?è·ˉ_????|1????±?è?¨???

二、collections模块

在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。

  1. namedtuple: 生成可以使用名字来访问元素内容的tuple
  2. deque: 双端队列,可以快速的从另外一侧追加和推出对象
  3. Counter: 计数器,主要用来计数
  4. OrderedDict: 有序字典
  5. 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
???????????| - ???é¥??12????|1????±??¥?è·ˉ_????|1????±?_????|1????±?è?¨???

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

其他详细内容

??? ?? ?? è§????????? ??ˉ?????ˉ????????????? - ???é¥??12????|1????±??¥?è·ˉ_????|1????±?_????|1????±?è?¨???

三、time模块

和时间有关系的我们就要用到时间模块。在使用模块之前,应该首先导入这个模块。

常用方法:

  1. time.sleep(secs):(线程)推迟指定的时间运行,单位为秒;
  2. time.time():获取当前时间戳;

3.1 表示时间的三种方式

在Python中,通常有这三种方式来表示时间:时间戳、元组(struct_time)、格式化的时间字符串:

  1. 时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。

  2. 格式化的时间字符串(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 当前时区的名称
    %% %号本身
    
  3. 元组(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)

小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的。

?? è|????è|?é¥??12 - ???é¥??12????|1????±??¥?è·ˉ_????|1????±?è?¨???

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

??????è|????è?a?·±??? - ???é¥??12????|1????±??¥?è·ˉ_????|1????±?è?¨???

结构化时间-->格式化字符串时间:

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

????o?è??é¥??12?? ?°±??ˉ???????oo??| - ???é¥??12????|1????±??¥?è·ˉ_????|1????±?è?¨???

四、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表示周一
è°??2??????? - ???é¥??12????|1????±??¥?è·ˉ_????|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
?1′?o??o?èˉ¥????1′?o????? ·?-????????1??o?????1′èˉ′ - ??¨?o??°±è|??????¨?o????? ·?-? ?????¥?o??????¨???èˉ′ a??_???????¤′è?¨???