如何获取 Python 计时对象以在函数和装饰器范围内保留适当的信息
how to get a Python timing object to retain appropriate information within function and decorator scopes
我正在编写一个模块,以便在 Python 程序中快速轻松地计时。这个想法是可以在整个代码中创建小的时钟实例。这些时钟可用作可以启动、停止、再次启动和查询的对象。实例化的任何时钟都被添加到所有时钟的模块列表中。在程序结束时,可以请求此列表的所有时钟的打印输出(所有时钟的列表或所有类似时钟的平均值)。
我已经做了很多工作,但函数的时间安排仍然给我带来困难。具体来说,当函数 1 和 1 的测量时间应分别为 ~3 秒和 ~4 秒时,使用显式时钟或使用装饰器将函数测量的时间测量为 0。
我怀疑我没有以适当的方式保留时钟属性_startTimeTmp
(为了内部计算的目的可以重置它)。
我非常感谢有关使计时器正常工作的一些指导。我对如何解决它有点困惑!
我知道代码可能看起来有点长,但我已尽我所能将其最小化,同时又不影响我正在尝试做的整体愿景(这样提出的任何建议都不会'删除关键功能)。我确实认为至少它是如何工作的相当清楚。
模块(shijian.py):
from __future__ import division
import os
import time
import uuid as uuid
import datetime
import inspect
import functools
def _main():
global clocks
clocks = Clocks()
def time_UTC(
style = None
):
return(
style_datetime_object(
datetimeObject = datetime.datetime.utcnow(),
style = style
)
)
def style_datetime_object(
datetimeObject = None,
style = "YYYY-MM-DDTHHMMSS"
):
# filename safe
if style == "YYYY-MM-DDTHHMMSSZ":
return(datetimeObject.strftime('%Y-%m-%dT%H%M%SZ'))
# microseconds
elif style == "YYYY-MM-DDTHHMMSSMMMMMMZ":
return(datetimeObject.strftime('%Y-%m-%dT%H%M%S%fZ'))
# elegant
elif style == "YYYY-MM-DD HH:MM:SS UTC":
return(datetimeObject.strftime('%Y-%m-%d %H:%M:%SZ'))
# UNIX time in seconds with second fraction
elif style == "UNIX time S.SSSSSS":
return(
(datetimeObject -\
datetime.datetime.utcfromtimestamp(0)).total_seconds()
)
# UNIX time in seconds rounded
elif style == "UNIX time S":
return(
int((datetimeObject -\
datetime.datetime.utcfromtimestamp(0)).total_seconds())
)
# filename safe
else:
return(datetimeObject.strftime('%Y-%m-%dT%H%M%SZ'))
def UID():
return(str(uuid.uuid4()))
class Clock(object):
def __init__(
self,
name = None,
start = True
):
self._name = name
self._start = start # Boolean start clock on instantiation
self._startTime = None # internal (value to return)
self._startTimeTmp = None # internal (value for calculations)
self._stopTime = None # internal (value to return)
self._updateTime = None # internal
# If no name is specified, generate a unique one.
if self._name is None:
self._name = UID()
# If a global clock list is detected, add a clock instance to it.
if "clocks" in globals():
clocks.add(self)
self.reset()
if self._start:
self.start()
def start(self):
self._startTimeTmp = datetime.datetime.utcnow()
self._startTime = datetime.datetime.utcnow()
def stop(self):
self._updateTime = None
self._startTimeTmp = None
self._stopTime = datetime.datetime.utcnow()
# Update the clock accumulator.
def update(self):
if self._updateTime:
self.accumulator += (
datetime.datetime.utcnow() - self._updateTime
)
else:
self.accumulator += (
datetime.datetime.utcnow() - self._startTimeTmp
)
self._updateTime = datetime.datetime.utcnow()
def reset(self):
self.accumulator = datetime.timedelta(0)
self._startTimeTmp = None
# If the clock has a start time, add the difference between now and the
# start time to the accumulator and return the accumulation. If the clock
# does not have a start time, return the accumulation.
def elapsed(self):
if self._startTimeTmp:
self.update()
return(self.accumulator)
def name(self):
return(self._name)
def time(self):
return(self.elapsed().total_seconds())
def startTime(self):
if self._startTime:
return(style_datetime_object(datetimeObject = self._startTime))
else:
return("none")
def stopTime(self):
if self._stopTime:
return(style_datetime_object(datetimeObject = self._stopTime))
else:
return("none")
def report(
self
):
string = "clock attribute".ljust(39) + "value"
string += "\nname".ljust(40) + self.name()
string += "\ntime start (s)".ljust(40) + self.startTime()
string += "\ntime stop (s)".ljust(40) + self.stopTime()
string += "\ntime elapsed (s)".ljust(40) + str(self.time())
string += "\n"
return(string)
def printout(self):
print(self.report())
def timer(function):
#@functools.wraps(function)
def decoration(
*args,
**kwargs
):
arguments = inspect.getcallargs(function, *args, **kwargs)
clock = Clock(name = function.__name__)
result = function(*args, **kwargs)
clock.stop()
return(decoration)
class Clocks(object):
def __init__(
self
):
self._listOfClocks = []
self._defaultReportStyle = "statistics"
def add(
self,
clock
):
self._listOfClocks.append(clock)
def report(
self,
style = None
):
if style is None:
style = self._defaultReportStyle
if self._listOfClocks != []:
if style == "statistics":
# Create a dictionary of clock types with corresponding lists of
# times for all instances.
dictionaryOfClockTypes = {}
# Get the names of all clocks and add them to the dictionary.
for clock in self._listOfClocks:
dictionaryOfClockTypes[clock.name()] = []
# Record the values of all clocks for their respective names in
# the dictionary.
for clock in self._listOfClocks:
dictionaryOfClockTypes[clock.name()].append(clock.time())
# Create a report, calculating the average value for each clock
# type.
string = "clock type".ljust(39) + "mean time (s)"
for name, values in dictionaryOfClockTypes.iteritems():
string += "\n" +\
str(name).ljust(39) + str(sum(values)/len(values))
string += "\n"
elif style == "full":
# Create a report, listing the values of all clocks.
string = "clock".ljust(39) + "time (s)"
for clock in self._listOfClocks:
string += "\n" +\
str(clock.name()).ljust(39) + str(clock.time())
string += "\n"
else:
string = "no clocks"
return(string)
def printout(
self,
style = None
):
if style is None:
style = self._defaultReportStyle
print(self.report(style = style))
_main()
主要代码示例(examples.py):
import shijian
import time
import inspect
def main():
print("create clock alpha")
alpha = shijian.Clock(name = "alpha")
print("clock alpha start time: {time}".format(time = alpha.startTime()))
print("sleep 2 seconds")
time.sleep(2)
print("clock alpha current time (s): {time}".format(time = alpha.time()))
print("\ncreate clock beta")
beta = shijian.Clock(name = "beta")
print("clock beta start time: {time}".format(time = beta.startTime()))
print("clock beta stop time: {time}".format(time = beta.stopTime()))
print("sleep 2 seconds")
time.sleep(2)
print("clock beta current time (s): {time}".format(time = beta.time()))
print("stop clock beta")
beta.stop()
print("clock beta start time: {time}".format(time = beta.startTime()))
print("clock beta stop time: {time}".format(time = beta.stopTime()))
print("sleep 2 seconds")
time.sleep(2)
print("clock beta start time: {time}".format(time = beta.startTime()))
print("clock beta stop time: {time}".format(time = beta.stopTime()))
print("clock beta current time (s): {time}".format(time = beta.time()))
print("\nclock beta printout:\n")
beta.printout()
print("create two gamma clocks")
gamma = shijian.Clock(name = "gamma")
gamma = shijian.Clock(name = "gamma")
print("sleep 2 seconds")
time.sleep(2)
print("\ncreate two unnamed clocks")
delta = shijian.Clock()
epsilon = shijian.Clock()
print("sleep 2 seconds")
time.sleep(2)
print("\nrun function 1 (which is timed using internal clocks)")
function1()
print("\nrun function 2 (which is timed using a decorator)")
function2()
print("\nclocks full printout:\n")
shijian.clocks.printout(style = "full")
print("clocks statistics printout:\n")
shijian.clocks.printout()
def function1():
functionName = inspect.stack()[0][3]
clock = shijian.Clock(name = functionName)
print("initiate {functionName}".format(functionName = functionName))
time.sleep(3)
print("terminate {functionName}".format(functionName = functionName))
clock.stop()
@shijian.timer
def function2():
functionName = inspect.stack()[0][3]
print("initiate {functionName}".format(functionName = functionName))
time.sleep(4)
print("terminate {functionName}".format(functionName = functionName))
if __name__ == '__main__':
main()
示例终端输出:
create clock alpha
clock alpha start time: 2015-01-03T090124Z
sleep 2 seconds
clock alpha current time (s): 2.000887
create clock beta
clock beta start time: 2015-01-03T090126Z
clock beta stop time: none
sleep 2 seconds
clock beta current time (s): 2.002123
stop clock beta
clock beta start time: 2015-01-03T090126Z
clock beta stop time: 2015-01-03T090128Z
sleep 2 seconds
clock beta start time: 2015-01-03T090126Z
clock beta stop time: 2015-01-03T090128Z
clock beta current time (s): 2.002123
clock beta printout:
clock attribute value
name beta
time start (s) 2015-01-03T090126Z
time stop (s) 2015-01-03T090128Z
time elapsed (s) 2.002123
create two gamma clocks
sleep 2 seconds
create two unnamed clocks
sleep 2 seconds
run function 1 (which is timed using internal clocks)
initiate function1
terminate function1
run function 2 (which is timed using a decorator)
initiate function2
terminate function2
clocks full printout:
clock time (s)
alpha 17.023659
beta 2.002123
gamma 11.018138
gamma 11.018138
1919f9de-85ce-48c9-b1c8-5164f3a2633e 9.017148
d24c818c-f4e6-48d0-ad72-f050a5cf86d3 9.017027
function1 0.0
function2 0.0
clocks statistics printout:
clock type mean time (s)
function1 0.0
function2 0.0
1919f9de-85ce-48c9-b1c8-5164f3a2633e 9.017283
beta 2.002123
alpha 17.023834
d24c818c-f4e6-48d0-ad72-f050a5cf86d3 9.017163
gamma 11.0182835
Clock
在 stop
ped 时不会得到 update
d。最小修复是:
def stop(self):
self.update()
self._updateTime = None
self._startTimeTmp = None
self._stopTime = datetime.datetime.utcnow()
您还有其他三个错误:
- 您应该通过身份 (
if foo is not None
) 而不是真实性 (if foo
) 来测试 None
,以避免 False
-y 值不是 None
;
shijian.timer
不会 return result
,所以尽管计时会起作用,但您会破坏任何期望来自修饰函数的 return 的代码;和
- 如果你想让代码在Python 2和3中工作,你不能使用
dict.iteritems
,后者不存在。如果您只希望它在 Python 2 中工作,请 from __future__ import print_function
或使用 print whatever
而不是 print(whatever)
.
此外,您的代码根本不符合 the style guide(或者,更糟的是,甚至内部一致 - 例如,将 Clock.start
的定义与 Clock.report
的定义进行比较) .
在设计和功能上也有改进的空间(例如 Clock.name
可以是 @property
,我会将 table 打印与结果生成分开。一旦满足以下条件,您应该考虑为 Code Review 提交代码:
- 完成;
- 已测试;和
- 符合风格指南
(您可能会发现使用 pylint
对后者有帮助)。
最后,我假设您这样做是出于学习目的,而不是因为您需要功能,如 Python has its own profilers.
我正在编写一个模块,以便在 Python 程序中快速轻松地计时。这个想法是可以在整个代码中创建小的时钟实例。这些时钟可用作可以启动、停止、再次启动和查询的对象。实例化的任何时钟都被添加到所有时钟的模块列表中。在程序结束时,可以请求此列表的所有时钟的打印输出(所有时钟的列表或所有类似时钟的平均值)。
我已经做了很多工作,但函数的时间安排仍然给我带来困难。具体来说,当函数 1 和 1 的测量时间应分别为 ~3 秒和 ~4 秒时,使用显式时钟或使用装饰器将函数测量的时间测量为 0。
我怀疑我没有以适当的方式保留时钟属性_startTimeTmp
(为了内部计算的目的可以重置它)。
我非常感谢有关使计时器正常工作的一些指导。我对如何解决它有点困惑!
我知道代码可能看起来有点长,但我已尽我所能将其最小化,同时又不影响我正在尝试做的整体愿景(这样提出的任何建议都不会'删除关键功能)。我确实认为至少它是如何工作的相当清楚。
模块(shijian.py):
from __future__ import division
import os
import time
import uuid as uuid
import datetime
import inspect
import functools
def _main():
global clocks
clocks = Clocks()
def time_UTC(
style = None
):
return(
style_datetime_object(
datetimeObject = datetime.datetime.utcnow(),
style = style
)
)
def style_datetime_object(
datetimeObject = None,
style = "YYYY-MM-DDTHHMMSS"
):
# filename safe
if style == "YYYY-MM-DDTHHMMSSZ":
return(datetimeObject.strftime('%Y-%m-%dT%H%M%SZ'))
# microseconds
elif style == "YYYY-MM-DDTHHMMSSMMMMMMZ":
return(datetimeObject.strftime('%Y-%m-%dT%H%M%S%fZ'))
# elegant
elif style == "YYYY-MM-DD HH:MM:SS UTC":
return(datetimeObject.strftime('%Y-%m-%d %H:%M:%SZ'))
# UNIX time in seconds with second fraction
elif style == "UNIX time S.SSSSSS":
return(
(datetimeObject -\
datetime.datetime.utcfromtimestamp(0)).total_seconds()
)
# UNIX time in seconds rounded
elif style == "UNIX time S":
return(
int((datetimeObject -\
datetime.datetime.utcfromtimestamp(0)).total_seconds())
)
# filename safe
else:
return(datetimeObject.strftime('%Y-%m-%dT%H%M%SZ'))
def UID():
return(str(uuid.uuid4()))
class Clock(object):
def __init__(
self,
name = None,
start = True
):
self._name = name
self._start = start # Boolean start clock on instantiation
self._startTime = None # internal (value to return)
self._startTimeTmp = None # internal (value for calculations)
self._stopTime = None # internal (value to return)
self._updateTime = None # internal
# If no name is specified, generate a unique one.
if self._name is None:
self._name = UID()
# If a global clock list is detected, add a clock instance to it.
if "clocks" in globals():
clocks.add(self)
self.reset()
if self._start:
self.start()
def start(self):
self._startTimeTmp = datetime.datetime.utcnow()
self._startTime = datetime.datetime.utcnow()
def stop(self):
self._updateTime = None
self._startTimeTmp = None
self._stopTime = datetime.datetime.utcnow()
# Update the clock accumulator.
def update(self):
if self._updateTime:
self.accumulator += (
datetime.datetime.utcnow() - self._updateTime
)
else:
self.accumulator += (
datetime.datetime.utcnow() - self._startTimeTmp
)
self._updateTime = datetime.datetime.utcnow()
def reset(self):
self.accumulator = datetime.timedelta(0)
self._startTimeTmp = None
# If the clock has a start time, add the difference between now and the
# start time to the accumulator and return the accumulation. If the clock
# does not have a start time, return the accumulation.
def elapsed(self):
if self._startTimeTmp:
self.update()
return(self.accumulator)
def name(self):
return(self._name)
def time(self):
return(self.elapsed().total_seconds())
def startTime(self):
if self._startTime:
return(style_datetime_object(datetimeObject = self._startTime))
else:
return("none")
def stopTime(self):
if self._stopTime:
return(style_datetime_object(datetimeObject = self._stopTime))
else:
return("none")
def report(
self
):
string = "clock attribute".ljust(39) + "value"
string += "\nname".ljust(40) + self.name()
string += "\ntime start (s)".ljust(40) + self.startTime()
string += "\ntime stop (s)".ljust(40) + self.stopTime()
string += "\ntime elapsed (s)".ljust(40) + str(self.time())
string += "\n"
return(string)
def printout(self):
print(self.report())
def timer(function):
#@functools.wraps(function)
def decoration(
*args,
**kwargs
):
arguments = inspect.getcallargs(function, *args, **kwargs)
clock = Clock(name = function.__name__)
result = function(*args, **kwargs)
clock.stop()
return(decoration)
class Clocks(object):
def __init__(
self
):
self._listOfClocks = []
self._defaultReportStyle = "statistics"
def add(
self,
clock
):
self._listOfClocks.append(clock)
def report(
self,
style = None
):
if style is None:
style = self._defaultReportStyle
if self._listOfClocks != []:
if style == "statistics":
# Create a dictionary of clock types with corresponding lists of
# times for all instances.
dictionaryOfClockTypes = {}
# Get the names of all clocks and add them to the dictionary.
for clock in self._listOfClocks:
dictionaryOfClockTypes[clock.name()] = []
# Record the values of all clocks for their respective names in
# the dictionary.
for clock in self._listOfClocks:
dictionaryOfClockTypes[clock.name()].append(clock.time())
# Create a report, calculating the average value for each clock
# type.
string = "clock type".ljust(39) + "mean time (s)"
for name, values in dictionaryOfClockTypes.iteritems():
string += "\n" +\
str(name).ljust(39) + str(sum(values)/len(values))
string += "\n"
elif style == "full":
# Create a report, listing the values of all clocks.
string = "clock".ljust(39) + "time (s)"
for clock in self._listOfClocks:
string += "\n" +\
str(clock.name()).ljust(39) + str(clock.time())
string += "\n"
else:
string = "no clocks"
return(string)
def printout(
self,
style = None
):
if style is None:
style = self._defaultReportStyle
print(self.report(style = style))
_main()
主要代码示例(examples.py):
import shijian
import time
import inspect
def main():
print("create clock alpha")
alpha = shijian.Clock(name = "alpha")
print("clock alpha start time: {time}".format(time = alpha.startTime()))
print("sleep 2 seconds")
time.sleep(2)
print("clock alpha current time (s): {time}".format(time = alpha.time()))
print("\ncreate clock beta")
beta = shijian.Clock(name = "beta")
print("clock beta start time: {time}".format(time = beta.startTime()))
print("clock beta stop time: {time}".format(time = beta.stopTime()))
print("sleep 2 seconds")
time.sleep(2)
print("clock beta current time (s): {time}".format(time = beta.time()))
print("stop clock beta")
beta.stop()
print("clock beta start time: {time}".format(time = beta.startTime()))
print("clock beta stop time: {time}".format(time = beta.stopTime()))
print("sleep 2 seconds")
time.sleep(2)
print("clock beta start time: {time}".format(time = beta.startTime()))
print("clock beta stop time: {time}".format(time = beta.stopTime()))
print("clock beta current time (s): {time}".format(time = beta.time()))
print("\nclock beta printout:\n")
beta.printout()
print("create two gamma clocks")
gamma = shijian.Clock(name = "gamma")
gamma = shijian.Clock(name = "gamma")
print("sleep 2 seconds")
time.sleep(2)
print("\ncreate two unnamed clocks")
delta = shijian.Clock()
epsilon = shijian.Clock()
print("sleep 2 seconds")
time.sleep(2)
print("\nrun function 1 (which is timed using internal clocks)")
function1()
print("\nrun function 2 (which is timed using a decorator)")
function2()
print("\nclocks full printout:\n")
shijian.clocks.printout(style = "full")
print("clocks statistics printout:\n")
shijian.clocks.printout()
def function1():
functionName = inspect.stack()[0][3]
clock = shijian.Clock(name = functionName)
print("initiate {functionName}".format(functionName = functionName))
time.sleep(3)
print("terminate {functionName}".format(functionName = functionName))
clock.stop()
@shijian.timer
def function2():
functionName = inspect.stack()[0][3]
print("initiate {functionName}".format(functionName = functionName))
time.sleep(4)
print("terminate {functionName}".format(functionName = functionName))
if __name__ == '__main__':
main()
示例终端输出:
create clock alpha
clock alpha start time: 2015-01-03T090124Z
sleep 2 seconds
clock alpha current time (s): 2.000887
create clock beta
clock beta start time: 2015-01-03T090126Z
clock beta stop time: none
sleep 2 seconds
clock beta current time (s): 2.002123
stop clock beta
clock beta start time: 2015-01-03T090126Z
clock beta stop time: 2015-01-03T090128Z
sleep 2 seconds
clock beta start time: 2015-01-03T090126Z
clock beta stop time: 2015-01-03T090128Z
clock beta current time (s): 2.002123
clock beta printout:
clock attribute value
name beta
time start (s) 2015-01-03T090126Z
time stop (s) 2015-01-03T090128Z
time elapsed (s) 2.002123
create two gamma clocks
sleep 2 seconds
create two unnamed clocks
sleep 2 seconds
run function 1 (which is timed using internal clocks)
initiate function1
terminate function1
run function 2 (which is timed using a decorator)
initiate function2
terminate function2
clocks full printout:
clock time (s)
alpha 17.023659
beta 2.002123
gamma 11.018138
gamma 11.018138
1919f9de-85ce-48c9-b1c8-5164f3a2633e 9.017148
d24c818c-f4e6-48d0-ad72-f050a5cf86d3 9.017027
function1 0.0
function2 0.0
clocks statistics printout:
clock type mean time (s)
function1 0.0
function2 0.0
1919f9de-85ce-48c9-b1c8-5164f3a2633e 9.017283
beta 2.002123
alpha 17.023834
d24c818c-f4e6-48d0-ad72-f050a5cf86d3 9.017163
gamma 11.0182835
Clock
在 stop
ped 时不会得到 update
d。最小修复是:
def stop(self):
self.update()
self._updateTime = None
self._startTimeTmp = None
self._stopTime = datetime.datetime.utcnow()
您还有其他三个错误:
- 您应该通过身份 (
if foo is not None
) 而不是真实性 (if foo
) 来测试None
,以避免False
-y 值不是None
; shijian.timer
不会return result
,所以尽管计时会起作用,但您会破坏任何期望来自修饰函数的 return 的代码;和- 如果你想让代码在Python 2和3中工作,你不能使用
dict.iteritems
,后者不存在。如果您只希望它在 Python 2 中工作,请from __future__ import print_function
或使用print whatever
而不是print(whatever)
.
此外,您的代码根本不符合 the style guide(或者,更糟的是,甚至内部一致 - 例如,将 Clock.start
的定义与 Clock.report
的定义进行比较) .
在设计和功能上也有改进的空间(例如 Clock.name
可以是 @property
,我会将 table 打印与结果生成分开。一旦满足以下条件,您应该考虑为 Code Review 提交代码:
- 完成;
- 已测试;和
- 符合风格指南
(您可能会发现使用 pylint
对后者有帮助)。
最后,我假设您这样做是出于学习目的,而不是因为您需要功能,如 Python has its own profilers.