5.2. time — 时间模块

未匹配的标注

目的:控制时钟时间。

时间模块 time 提供了几种不同类型时间的方法,每一种对不同目的都有用。
标准系统通过调用 time() (模块)报告系统挂钟时间(”wall clock” 一般指程序执行时间)。 单调时钟模块monotonic()用于估算长时间运行的程序的运行时间,因为即使系统时间发生了变化,它也保证不会后退。 对于程序测试,perf_counter() (模块) 提供了高分辨率的时钟访问方式,使短时间的测试更准确。CPU时间可以通过 clock() 获得,而 process_time() 则会返回处理器时间和系统时间的组合。

python3.8后使用perf_counter()或者process_time()而不能使用clock()

笔记

此实现方式基于公开的用于控制日期和时间的C库函数。由于它们底层与C绑定在一起,所以一些细节(比如时间的开始和支持的最大日期值)是基于特定平台的。有关完整的详细信息,请参阅库文档。

比较时钟

时钟的实现细节因平台而异。使用“get_clock_info()”来访问关于当前方式的基本信息,包括时钟的分辨率。

time_get_clock_info.py

import textwrap
import time

available_clocks = [
    ('clock', time.clock),
    ('monotonic', time.monotonic),
    ('perf_counter', time.perf_counter),
    ('process_time', time.process_time),
    ('time', time.time),
]

for clock_name, func in available_clocks:
    print(textwrap.dedent('''\
    {name}:
        adjustable    : {info.adjustable}
        implementation: {info.implementation}
        monotonic     : {info.monotonic}
        resolution    : {info.resolution}
        current       : {current}
    ''').format(
        name=clock_name,
        info=time.get_clock_info(clock_name),
        current=func())
    )

Mac OS X 的输出显示其 monotonic 和 perf_counter clocks 模块调用相同的底层。

$ python3 time_get_clock_info.py

clock:
    adjustable    : False
    implementation: clock()
    monotonic     : True
    resolution    : 1e-06
    current       : 0.046796

monotonic:
    adjustable    : False
    implementation: mach_absolute_time()
    monotonic     : True
    resolution    : 1e-09
    current       : 716028.526210432

perf_counter:
    adjustable    : False
    implementation: mach_absolute_time()
    monotonic     : True
    resolution    : 1e-09
    current       : 716028.526241605

process_time:
    adjustable    : False
    implementation: getrusage(RUSAGE_SELF)
    monotonic     : True
    resolution    : 1e-06
    current       : 0.046946999999999996

time:
    adjustable    : True
    implementation: gettimeofday()
    monotonic     : False
    resolution    : 1e-06
    current       : 1521404584.966362

挂钟时间

time 模块的核心函数之一是 time() ,它可以把从 「epoch」 开始之后的秒数以浮点数的格式返回。

time_time.py

import time

print('The time is:', time.time())

时元 (epoch) 是测量时间的开始,对于 Unix 系统来说,时元是1970年1月1日0点。 虽然该数值一直是一个浮点数,但是它的实际精度是依赖于平台的。

$ python3 time_time.py

The time is: 1521404585.0243158

虽然浮点表示在存储或者比较日期时非常有效,但是,在生成人类可读的表示时就显得有点力不从心。对于记录或者打印时间, ctime() 可能会更有效。

time_ctime.py

import time

print('The time is      :', time.ctime())
later = time.time() + 15
print('15 secs from now :', time.ctime(later))

本例中调用的第二个 print() 函数显示了如何使用 ctime() 函数去格式化时除当前时间以外的间值。

$ python3 time_ctime.py

The time is      : Sun Mar 18 16:23:05 2018
15 secs from now : Sun Mar 18 16:23:20 2018

单调时钟

因为 time() 函数返回的时间值是系统时钟,并且,为了在多台计算机之间同步时钟,系统时钟可以被用户或者是系统服务更改;所以,在重复调用 time() 函数时产生的时间值可能会有前后波动。在测量持续时间或者使用这些时间进行计算的时候,这可能会导致意料之外的行为。通过使用 monotonic() 函数就可以避免这些情况,因为 monotonic() 函数总是返回前向的时间值。

time_monotonic.py

import time

start = time.monotonic()
time.sleep(0.1)
end = time.monotonic()
print('start : {:>9.2f}'.format(start))
print('end   : {:>9.2f}'.format(end))
print('span  : {:>9.2f}'.format(end - start))

我们并没有定义单调时钟的起始点。因此,只有在使用其它时钟值进行计算时,单调时钟的返回值才会有用。在这个例子中,睡眠的持续时间就是用 monotonic() 函数测量的。

$ python3 time_monotonic.py

start : 716028.72
end   : 716028.82
span  :      0.10

处理器时钟时间

time() 函数返回的是挂钟时间, clock() 函数返回的是处理器时钟时间。 clock() 函数的返回值反映了程序运行时使用的实际时间。

time_clock.py

import hashlib
import time

# 用于计算 md5校验和 的数据
data = open(__file__, 'rb').read()

for i in range(5):
    h = hashlib.sha1()
    print(time.ctime(), ': {:0.3f} {:0.3f}'.format(
        time.time(), time.clock()))
    for i in range(300000):
        h.update(data)
    cksum = h.digest()

在这个例子中,对于循环中的每一次迭代,格式化的 ctime() 函数返回值、 time() 函数返回的浮点型时间值以及 clock() 函数的返回值会依次被打印出来。

注释

如果你想要在自己的系统上运行这个例子,你可能需要想办法增加单次内循环的循环周期或者显著地增加循环的总次数,才可以真正地看到时间差异。(如果循环周期太短,循环次数太少的话,返回的时间值可能还没有来得及变化,整个程序就结束了。)

$ python3 time_clock.py

Sun Mar 18 16:23:05 2018 : 1521404585.328 0.051
Sun Mar 18 16:23:05 2018 : 1521404585.632 0.349
Sun Mar 18 16:23:05 2018 : 1521404585.935 0.648
Sun Mar 18 16:23:06 2018 : 1521404586.234 0.943
Sun Mar 18 16:23:06 2018 : 1521404586.539 1.244

通常情况下,如果程序什么事情没有做,处理器时钟就不会滴答计时。

time_clock_sleep.py

import time

template = '{} - {:0.2f} - {:0.2f}'

print(template.format(
    time.ctime(), time.time(), time.clock())
)

for i in range(3, 0, -1):
    print('Sleeping', i)
    time.sleep(i)
    print(template.format(
        time.ctime(), time.time(), time.clock())
    )

在这个例子中,在每一次迭代后进入休眠状态时,循环的工作量很小。 即使应用程序处于睡眠状态, time() 函数的返回值也会增加,但 clock() 的函数返回值不会增加。

$ python3 -u time_clock_sleep.py

Sun Mar 18 16:23:06 2018 - 1521404586.89 - 0.04
Sleeping 3
Sun Mar 18 16:23:09 2018 - 1521404589.90 - 0.04
Sleeping 2
Sun Mar 18 16:23:11 2018 - 1521404591.90 - 0.04
Sleeping 1
Sun Mar 18 16:23:12 2018 - 1521404592.90 - 0.04

调用 sleep() 函数会从当前线程获得控制权,并且要求它等待系统将其唤醒。如果一个程序只有一个线程,这会有效地阻止应用程序,并使应用程序不做任何工作。

性能计数器

有一个高分辨率的单调时钟来衡量性能是非常重要的。要想确定最佳时钟数据源,需要一些特定于平台的知识,这些知识由 Python 中的 perf_counter() 提供。

time_perf_counter.py

import hashlib
import time

# 用于计算 md5校验和 的数据
data = open(__file__, 'rb').read()

loop_start = time.perf_counter()

for i in range(5):
    iter_start = time.perf_counter()
    h = hashlib.sha1()
    for i in range(300000):
        h.update(data)
    cksum = h.digest()
    now = time.perf_counter()
    loop_elapsed = now - loop_start
    iter_elapsed = now - iter_start
    print(time.ctime(), ': {:0.3f} {:0.3f}'.format(
        iter_elapsed, loop_elapsed))

monotonic() 函数一样, perf_counter() 函数的时元(epoch)是未定义的。并且,函数的这些返回值用于比较和计算,而不是用作绝对时间。

$ python3 time_perf_counter.py

Sun Mar 18 16:23:13 2018 : 0.378 0.378
Sun Mar 18 16:23:13 2018 : 0.376 0.754
Sun Mar 18 16:23:14 2018 : 0.372 1.126
Sun Mar 18 16:23:14 2018 : 0.376 1.502
Sun Mar 18 16:23:14 2018 : 0.376 1.879

时间组件

按秒计数,把秒作为单位来存储时间在某些场合很适用,但有时候程序需要访问的(或者说程序感兴趣的)是日期的不同单位所对应的各个部分(比如,某年、某月等)。为了让日期的各个部分便于访问, time 模块定义了 struct_time ,它以各个部分分离的格式来存储日期和时间值。 有些函数以 struct_time 类型的数值作为参数,而不是浮点型的数值。

time_struct.py

import time

def show_struct(s):
    print('  tm_year :', s.tm_year)
    print('  tm_mon  :', s.tm_mon)
    print('  tm_mday :', s.tm_mday)
    print('  tm_hour :', s.tm_hour)
    print('  tm_min  :', s.tm_min)
    print('  tm_sec  :', s.tm_sec)
    print('  tm_wday :', s.tm_wday)
    print('  tm_yday :', s.tm_yday)
    print('  tm_isdst:', s.tm_isdst)

print('gmtime:')
show_struct(time.gmtime())
print('\nlocaltime:')
show_struct(time.localtime())
print('\nmktime:', time.mktime(time.localtime()))

gmtime() 函数返回 UTC (世界标准时间)中的当前时间。 localtime() 函数返回的是与当前时区相对应的当前时间 。 mktime() 接收一个 struct_time 型数值并将其转换为浮点型数值。

$ python3 time_struct.py

gmtime:
  tm_year : 2018
  tm_mon  : 3
  tm_mday : 18
  tm_hour : 20
  tm_min  : 23
  tm_sec  : 14
  tm_wday : 6
  tm_yday : 77
  tm_isdst: 0

localtime:
  tm_year : 2018
  tm_mon  : 3
  tm_mday : 18
  tm_hour : 16
  tm_min  : 23
  tm_sec  : 14
  tm_wday : 6
  tm_yday : 77
  tm_isdst: 1

mktime: 1521404594.0

与时区相关的工作

确定当前时间的函数依赖于时区设置,时区可以由程序设置,也可以使用系统的默认时区设置。改变时区并不会改变实际的时间,只是改变它的表现方式。

要想改变时区,请先设置环境变量 TZ ,然后调用 tzset() 。时区可以用很多细节来指定,直到夏令时的开始和结束时间。不过通常来讲,使用时区名称并且让底层库派生其它信息更容易一些。

这个示例程序将时区更改为几个不同的值,并且显示这些更改是如何影响 time 模块中的其它设置。

time_timezone.py

import time
import os

def show_zone_info():
    print('  TZ    :', os.environ.get('TZ', '(not set)'))
    print('  tzname:', time.tzname)
    print('  Zone  : {} ({})'.format(
        time.timezone, (time.timezone / 3600)))
    print('  DST   :', time.daylight)
    print('  Time  :', time.ctime())
    print()

print('Default :')
show_zone_info()

ZONES = [
    'GMT',
    'Europe/Amsterdam',
]

for zone in ZONES:
    os.environ['TZ'] = zone
    time.tzset()
    print(zone, ':')
    show_zone_info()

系统上用于准备示例的默认时区是 US/Eastern 。示例中的其它时区更改 tzname 、 daylight 标志和时区偏移值。

$ python3 time_timezone.py

Default :
  TZ    : (not set)
  tzname: ('EST', 'EDT')
  Zone  : 18000 (5.0)
  DST   : 1
  Time  : Sun Mar 18 16:23:14 2018

GMT :
  TZ    : GMT
  tzname: ('GMT', 'GMT')
  Zone  : 0 (0.0)
  DST   : 0
  Time  : Sun Mar 18 20:23:14 2018

Europe/Amsterdam :
  TZ    : Europe/Amsterdam
  tzname: ('CET', 'CEST')
  Zone  : -3600 (-1.0)
  DST   : 1
  Time  : Sun Mar 18 21:23:14 2018

解析和格式化时间

有两个函数—— strptime()strftime() ——可以使时间值在 struct_time 表示和字符串表示之间相互转换。有一个很长的格式说明列表可以用来支持不同格式的输入和输出。完整的列表记录在 time 模块的库文件中。

这个例子将当前时间从字符串格式转换成 struct_time 实例,然后又将其转换为字符串格式。

time_strptime.py

import time

def show_struct(s):
    print('  tm_year :', s.tm_year)
    print('  tm_mon  :', s.tm_mon)
    print('  tm_mday :', s.tm_mday)
    print('  tm_hour :', s.tm_hour)
    print('  tm_min  :', s.tm_min)
    print('  tm_sec  :', s.tm_sec)
    print('  tm_wday :', s.tm_wday)
    print('  tm_yday :', s.tm_yday)
    print('  tm_isdst:', s.tm_isdst)

now = time.ctime(1483391847.433716)
print('Now:', now)

parsed = time.strptime(now)
print('\nParsed:')
show_struct(parsed)

print('\nFormatted:',
      time.strftime("%a %b %d %H:%M:%S %Y", parsed))

输出字符串和输入字符串并不完全相同。这是因为输出的月份日期以0为前缀。

$ python3 time_strptime.py

Now: Mon Jan  2 16:17:27 2017

Parsed:
  tm_year : 2017
  tm_mon  : 1
  tm_mday : 2
  tm_hour : 16
  tm_min  : 17
  tm_sec  : 27
  tm_wday : 0
  tm_yday : 2
  tm_isdst: -1

Formatted: Mon Jan 02 16:17:27 2017

另请参阅

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/pymotw/time-tim...

译文地址:https://learnku.com/docs/pymotw/time-tim...

上一篇 下一篇
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~