Skip to content

结构、标识、系统、时间

前面我们介绍了一些 Python 的标准库,这里再学习几个后期会高频使用的标准库。

结构

collections 模块:这个模块我们在前面接触过,从里面导入 Iterable 可迭代对象、Iterator 迭代器、Generator 生成器等。它是 Python 标准库中的一个重要模块,提供了诸多非常方便的容器数据类型,它们在功能上扩展了 Python 的内置容器(如 dictlistsettuple),以满足特定场景的需求。

  • Counter 是一个计数器,用于统计可迭代对象中各元素的出现次数。Counter 返回一个字典,其中键为元素,值为元素的计数。
  • deque 是一个双端队列,支持在两端快速添加和删除元素,常用于队列和栈的实现。与列表相比,如果你需要在头尾添加和删除大量元素时,使用 deque 会表现出更好的性能。
  • defaultdict 是一种带有默认值的字典。在访问不存在的键时会自动为该键创建一个默认值,以避免 KeyError 错误。通常用于字典中元素初始化的情况。
  • OrderedDict 是一个有序字典,它记录了键值对插入的顺序,特别适合需要顺序的场景。Python 3.7+ 的 dict 本身已经保证插入顺序,因此 OrderedDict 的优势在 Python 3.7 以上的版本中相对较小。
  • namedtuple 是一个具名元组,可以用类似访问对象属性的方式来访问元组的字段。它提供了更高的可读性,适合用来表示简单的数据结构,如点坐标或数据库记录。
  • ChainMap 是一个链图,它可以将多个字典或映射组合在一起,以便将它们作为单个字典进行访问。适合在多层命名空间或配置文件的场景下使用。
python
# 从collections导入Counter计数器
from collections import Counter

c1 = Counter([1, 1, 1, 7, 8, 4, 4, 7, 7, 7])
print(c1)                 # 输出:Counter({7: 4, 1: 3, 4: 2, 8: 1})。注释:输出了列表中元素以及元素出现的次数。
print(c1.most_common(2))  # 输出:[(7, 4), (1, 3)]。注释:输出了元组中出现频率最高的前两个元素及其出现次数。
c2 = Counter([1, 1, 1, 7, 8, 4, 4, 7, 7, 1])
print(c2)                 # 输出:Counter({1: 4, 7: 3, 4: 2, 8: 1})。注释:输出了列表中元素出现的次数。
print(c1 == c2)           # 输出:False。注释:判断list1和list2的组成元素的个数是否相同。
python
# 从collections导入deque双端队列
from collections import deque

d = deque([1, 2, 3])
# 左侧添加元素0
d.appendleft(0)
# 右侧添加元素4
d.append(4)
print(d)  # 输出:deque([0, 1, 2, 3, 4])
python
# 从collections导入defaultdict默认字典
from collections import defaultdict

dd = defaultdict(int)
dd['a'] += 1
print(dd)  # 输出:defaultdict(<class 'int'>, {'a': 1})
python
1# 从collections导入OrderedDict有序字典
from collections import OrderedDict

od = OrderedDict()
od['a'] = 1
od['b'] = 2
print(od)  # 输出:OrderedDict([('a', 1), ('b', 2)])
python
# 从collections导入namedtuple命名元组
from collections import namedtuple

Card = namedtuple('Card', ('suite', 'face'))
c1 = Card('红桃', 5)
c2 = Card('草花', 9)
print(f'{c1.suite}{c1.face}')  # 输出:红桃5
print(f'{c2.suite}{c2.face}')  # 输出:草花9
python
# 从collections导入ChainMap链图
from collections import ChainMap

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
chain = ChainMap(dict1, dict2)
print(chain['b'])  # 输出:2

标识

uuid 模块:生成全局唯一标识符(Universal Unique IDentity)。该模块提供了四个用于生成 UUID 的函数:

  • uuid1():由 MAC 地址、当前时间戳、随机数生成,可以保证全球范围内的唯一性。注意可能暴露 MAC 地址信息,因此在某些情况下可能不适用。
  • uuid3(namespace, name):通过计算命名空间和名字的 MD5 哈希摘要(“指纹”)值得到,保证了不同命名空间和同一命名空间中不同名字的唯一性,如果是同一命名空间的同一名字则会生成相同的 UUID。
  • uuid4():由伪随机数生成 UUID,有一定的重复概率,该概率可以计算出来。不适合在真正需要全局唯一标识符的地方使用,但在大多数情况下,它足够随机,而且生成速度较快。
  • uuid5():生成 UUID 的方式与 uuid3 相同,只不过哈希函数用 SHA-1 取代了 MD5,使用更安全的哈希算法。
python
import uuid

# 因为uuid1关系到时间戳、随机数等,所以每次输出的ID都是不同的
print(uuid.uuid1().hex)  # 输出:622a8334baab11eaaa9c60f81da8d840
print(uuid.uuid1().hex)  # 输出:62b066debaab11eaaa9c60f81da8d840
print(uuid.uuid1().hex)  # 输出:642c0db0baab11eaaa9c60f81da8d840

系统

os 模块

os 模块:负责程序与操作系统交互,封装了常见的文件和目录操作。

python
import os

# 显示当前计算机CPU的核心数
print(os.cpu_count())
# 获取当前进程id
print(os.getpid())

# 获取当前的工作目录的绝对地址
print(os.getcwd())
# 切换工作目录
print(os.chdir('路径'))
# 以列表形式返回指定路径下的所有文件和文件夹
print(os.listdir('路径'))
# 返回程序所在文件的名称
print(os.path.basename(__file__))
# 根据程序所在文件的相对路径返回其绝对路径
print(os.path.abspath('.\test.py'))
# 返回程序所在文件的上一层的绝对路径
print(os.path.dirname(__file__))
# 返回程序所在文件的下一层的绝对路径
print(os.path.join(__file__), '下层文件夹名')
# 判断括号里的路径或文件是否存在,存在返回True,不存在返回False
print(os.path.exists('路径/文件.后缀名'))
# 返回文件大小(单位字节)
print(os.path.getsize('路径/文件.后缀名'))

# 递归创建文件夹(自动创建不存在的路径或文件夹,若路径和文件夹都存在,则必须加上exist_ok参数,否则会报错。注意文件夹名称大小写不敏感。)
os.makedirs('路径/文件夹', exist_ok=True)
# 重命名文件(重命名不存在的文件会报错)
os.rename('路径/旧文件名.后缀名', '路径/新文件名.后缀名')
# 删除文件(删除不存在的文件会报错)
os.remove('路径/文件名.后缀名')

# 执行cmd指令,返回值是脚本的退出状态码,只会有0(成功)、1、2三种状态。
# 执行单条命令
print(os.system('命令'))
# 执行多条命令
print(os.system('命令1 && 命令2 && 命令3 && ..'))
# 在指定路径下执行命令
print(os.system('cd 路径 && 命令'))
# 通过管道的方式来执行命令
md5_value = os.popen('命令')         # 注释:执行命令返回一个file-like对象。
print(md5_value.read().split()[0])  # 注释:获取结果返回值。

sys 模块

sys 模块:负责程序与 Python 解释器的交互,提供了一系列的函数和变量,用于操控 Python 的运行时环境。

python
import sys

# 返回操作系统平台名称
print(sys.platform)
# 获取当前环境中Python的版本信息
print(sys.version)
# 返回模块的搜索路径(环境变量),初始化时使用PYTHONPATH环境变量的值
print(sys.path)
# 返回系统导入的模块字段,key是模块名,value是模块 
print(sys.modules)
# 查看指定内容在内存中的引用次数
print(sys.getrefcount('内容'))
# 返回当前文件(程序被调用的文件)的绝对路径
print(sys.argv[0])
# 返回系统默认编码
print(sys.getdefaultencoding())

提醒

在 64 位的 Windows10 系统下 sys.platform 输出结果是 win32,这个 32 不是指的操作系统的位数,而是指的 Win32 API。这是为了保持 Python 在 Windows 平台上的兼容性,因为早期的 Python 版本基于 Win32 API 接口进行开发,后续保持了这个标识,以便所有版本的 Windows(包括 64 位)都可以通过相同的标识进行识别。

时间

时间术语

在日常生活中,我们可能会听到或看到有关于时间的一些术语,例如世界时区、格林尼治标准时间或世界时等,这里对它们的具体含义进行如下说明:

  • 世界时区,也叫 24 时区,它源于 1884 年在华盛顿召开的一次国际经度会议,此次会议规定将全球划分为 24 个时区,目的就是为了解决东西方之间的时间混乱。所划分的这 24 个时区分别是东西 12 区(西 7.5 经度)、西 11 ~ 1 区、中时区(零时区)、东 1 ~ 11 区、东西 12 区(东 7.5 经度),每个时区横跨经度 15 度,时间正好是 1 小时。‌例如,‌伦敦的地理位置在中时区,北京的地理位置在东八区,因此伦敦与北京的时差通常是 8 小时,由于地球总是自西向东自转,东边总比西边先看到太阳,因此东边的时间也总比西边的早,‌这意味着当北京时间是上午 10 点时,‌伦敦时间就是凌晨 2 点。‌

image-20240816094746217

  • 格林尼治标准时间(Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,它以本初子午线(‌0度经线)‌为基准,‌该线经过英国格林尼治天文台旧址,‌因此得名。格林尼治时间是世界时区的起点,也是国际上统一采用的标准时间之一,在日常口语和许多非技术文档中被广泛使用。格林尼治时间的制定对于全球的时间统一具有重要意义,使得不同地区的人们能够根据格林尼治时间来调整自己的时间,提高了信息的同步性。

  • 世界时(Universal Time,UT)是基于天体观察计算出来的时间。UT 本身是一个广泛的概念,其下包括 UT0、UT1、UT2 等。其中 UT0 是完全按照天体运行计算出来的时间,UT1 是在 UT0 的基础上做了一些调整,UT2 是在 UT0 和 UT1 的基础上又进行了一些调整。由于天体运行的一些不确定性(比如地球的自转并非匀速的,而是以复杂的方式进行着加速和减速),所以 UT 时间并不是均匀流过的。

  • 世界协调时时间(Universal Time Coordinated,UTC)是指全球统一使用的标准时间,它是由 TAI(International Atomic Time,国际原子时)以原子时为基础通过调整闰秒得到的。由于它十分精准,因此应用于许多互联网和万维网的标准中,例如,网络时间协议就是世界协调时在互联网中使用的一种方式。可以说,它的出现是现代社会对于精确计时的需要

image-20240816094206207

提醒

在不需要精确到秒的情况下,UTC 和 GMT 在大多数时间内是相同的,唯一的区别在于 UTC 通过添加或减去闰秒来与地球自转保持同步,而 GMT 也常常根据 UTC 的调整而调整。简单来说,GMT 更加民间更加口语,UTC 更加科学更加精确。

  • 中国国家标准时间(China Standard Time,CST)是中华人民共和国全境采用的国家标准时间,使用的是首都北京所在的区时,因此也称为“北京时间”。由于首都北京所在的时区是东八区,因此北京时间比世界协调时时间(Universal Time Coordinated,UTC)快 8 个小时,这也是 UTC+8 的含义。

image-20240816150810825

提醒

需要说明的是,北京时间并不是指北京所在(东经116.4°)的地方时间,而是东经120°经线上的时间(即是东八区区时),故北京时间比北京的地方时早约14分半钟。

时间结构

在 Python 中常用的时间格式有如下三种:

  • 时间戳,指的是从格林尼治时间 1970/01/01 00:00:00(北京时间 1970/01/01 08:00:00)到当前时间的总秒数。例如,1554722083.8482447 就是一个时间戳,表示格林尼治时间到当前时间的秒数之差。需要注意的是,时间戳和时区是没有关系的,时间戳在哪个时区都是一样的。因此如果将时间戳转换为某一时区时间的话,在没有默认处理的情况下,需要手动进行处理。例如,中国使用世界时区的东八时区作为标准时间,也就是 UTC+8,即北京时间。因此使用北京时间换算成时间戳时,在没有默认处理的情况下,你需要手动减去 8 小时,即 28800 秒。
  • struct_time 结构:形如 time.struct_time(tm_year=2019, tm_mon=4, tm_mday=8, tm_hour=19, tm_min=21, tm_sec=52, tm_wday=0, tm_yday=98, tm_isdst=0) 时间结构。参数如下:
tm_year:年
tm_mon:月
tm_mday:日
tm_hour:时
tm_min:分
tm_sec:秒
tm_wday:星期(0~6 --> 周一至周天)
tm_yday:当前是当年的第几天
tm_isdst:是否是夏令时
  • strf_time 结构:形如 2019-04-09 01:38:12 时间结构。参数如下:
%y:两位数的年份表示(00-99)
%Y:四位数的年份表示(0000-9999)
%m:月份(01-12)
%d:月内中的一天(01-31)
%H:24小时制小时数(0-23)
%h: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:当前时区的名称
%%:%号本身

time 模块

time 模块:Python 提供**处理时间(时分秒)**的标准库。

  • 转换时间戳:将时间戳转换为 struct_time 格式或 strf_time 格式。
python
import time

# 当前时间的时间戳(单位:秒)
stamp = time.time()
print(stamp)           # 输出:1554722083.8482447

# 将时间戳转换为struct_time格式
struct = time.localtime(stamp)
print(struct)          # 输出:time.struct_time(tm_year=2019, tm_mon=12, tm_mday=22...)
print(struct.tm_year)  # 输出:2019
print(struct.tm_mon)   # 输出:12
print(struct.tm_mday)  # 输出:22

# 将时间戳转换为strf_time格式
strf = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(stamp))
print(strf)            # 输出:2019-04-08 19:14:43
strf = time.strftime('%Y/%m/%d %H:%M:%S', time.localtime(stamp))
print(strf)            # 输出:2019/04/08 19:14:43
  • 转换 struct_time 格式:将 struct_time 格式转换为 strf_time 格式或时间戳。
python
import time

# struct_time时间
struct = time.struct_time((2019, 4, 8, 19, 14, 43, 0, 98, -1))

# 将struct_time时间转换成strf_time时间
strf = time.strftime('%Y-%m-%d %H:%M:%S', struct)
print(strf)   # 输出:2019-04-08 19:14:43
strf = time.strftime('%Y/%m/%d %H:%M:%S', struct)
print(strf)   # 输出:2019/04/08 19:14:43

# 将struct_time时间转换成时间戳
stamp = time.mktime(struct)
print(stamp)  # 输出:1554722083.0
  • 转换 strf_time 格式:将 strf_time 格式转换为 struct_time 格式或时间戳。
python
import time

# strf_time时间
strf = '2019-04-08 19:14:43'

# 将strf_time时间转换为struct_time时间
struct = time.strptime(strf, '%Y-%m-%d %H:%M:%S')
print(struct)  # 输出:time.struct_time(tm_year=2019, tm_mon=12, tm_mday=22...)

# 将strf_time时间转换成时间戳。
stamp = time.mktime(time.strptime(strf, '%Y-%m-%d %H:%M:%S'))
print(stamp)   # 输出:1554722083.0

重要

time 模块中还有一个常用的 sleep() 方法,功能就是让线程阻塞指定的时间。例如,time.sleep(10) 就是阻塞线程 10 秒的时间。

datetime 模块

datetime 模块:Python 提供**处理日期(年月日时分秒)**的标准库。

  • 获取当前时间的年月日,代码如下:
python
# 从datetime模块导入date处理年月日
from datetime import date

# 获取当前日期并创建日期类today
today = date.today()
print(today)               # 输出:2019-04-09。注释获取日期。
print(today.year)          # 输出:2019。注释:获取日期的年份。
print(today.month)         # 输出:4。注释:获取日期的月份。
print(today.day)           # 输出:9。注释:获取日期的号数。
print(today.isoweekday())  # 输出:2。注释:获取日期对应的星期。
  • 获取当前时间的年月日时分秒,代码如下:
python
# 从datetime模块导入datetime处理年月日时分秒
from datetime import datetime

# 获取当前日期时间并创建时间类now
now_dt = datetime.now()
print(now_dt)         # 输出:2019-04-09 02:27:47.907620。注释:获取完整的日期时间。
print(now_dt.date())  # 输出:2019-04-09。注释:获取日期部分。
print(now_dt.time())  # 输出:02:27:47.907620。注释:获取时分秒的部分。
  • 获取指定时间的时间戳、 struct_time 格式和 strf_time 格式,代码如下:
python
# 从datetime模块导入datetime处理年月日时分秒
from datetime import datetime

# 获取指定日期时间的datetime类型
oth_day = datetime(2019, 4, 9, 2, 27, 47)
print(oth_day.timestamp())                    # 输出:1554748067.0。注释:将datetime类型转换成strf_time格式。
print(oth_day.timetuple())                    # 输出:time.struct_time(tm_year=2019, ...)。注释:将datetime类型转换成struct_time格式。
print(oth_day.strftime('%Y-%m-%d %H:%M:%S'))  # 输出:2019-04-09 02:27:47。注释:将datetime类型转换成strf_time格式。
print(oth_day.strftime('%Y/%m/%d %H:%M:%S'))  # 输出:2019/04/09 02:27:47。注释:将datetime类型转换成strf_time格式。
  • 获取英文格式的 GMT 零时区的当前时间,代码如下:
python
from datetime import datetime, timezone

"""获取GMT零时区的当前时间"""
now_utc = datetime.now(timezone.utc)
formatted_time = now_utc.strftime('%a, %d %b %Y %H:%M:%S')
full_formatted_time = formatted_time + ' GMT'
print(full_formatted_time)  # 输出:Fri, 16 Aug 2024 08:11:46 GMT

"""定义一个特定的GMT时间"""
specific_time = datetime(2024, 7, 25, 6, 29, 13, tzinfo=timezone.utc)
formatted_time = specific_time.strftime('%a, %d %b %Y %H:%M:%S')
full_formatted_time = formatted_time + ' GMT'
print(full_formatted_time)  # 输出:Thu, 25 Jul 2024 06:29:13 GMT
  • 对时间进行增减操作,代码如下:
python
# 从datetime模块导入datetime处理年月日时分秒
from datetime import datetime

# 获取当前日期时间并创建时间类now
now_dt = datetime.now()
print(now_dt + datetime.timedelta(days=1))      # 输出:2019-04-10 02:27:47.907620。注释:日期加一天。
print(now_dt + datetime.timedelta(days=-2))     # 输出:2019-04-07 02:27:47.907620。注释:日期减两天。
print(now_dt + datetime.timedelta(minutes=50))  # 输出:2019-04-09 03:17:47.907620。注释:增加50分钟。
  • 对时间进行比较操作,代码如下:
python
# 从datetime模块导入datetime处理年月日时分秒
from datetime import datetime

# 获取指定日期时间的datetime类型
start_day = datetime.date(2021, 1, 1)
end_day = datetime.date(2021, 1, 2)
print(start_day, type(start_day))  # 输出:2021-01-01 <class 'datetime.date'>
print(end_day, type(start_day))    # 输出:2021-01-02 <class 'datetime.date'>
print(start_day > end_day)         # 输出:False。注释:datetime.date类型之间可以比较大小。
print(end_day > start_day)         # 输出:True。注释:datetime.date类型之间可以比较大小。
  • 对时间进行算术运算,代码如下:
python
# 从datetime模块导入datetime处理年月日时分秒
from datetime import datetime

# 获取指定日期时间的datetime类型
start_day = datetime.date(2021, 1, 1)
end_day = datetime.date(2021, 1, 2)
print(start_day, type(start_day))  # 输出:2021-01-01 <class 'datetime.date'>
print(end_day, type(start_day))    # 输出:2021-01-02 <class 'datetime.date'>
print(end_day - start_day)         # 输出:1 day, 0:00:00。注释:datetime.date类型之间可以相减。
print(start_day - end_day)         # 输出:-1 day, 0:00:00。注释:datetime.date类型之间可以相减。
print(start_day - start_day)       # 输出:0:00:00。注释:datetime.date类型之间可以相减。