Key difference:
requires obj.__enter__ / obj.__exit__.
works if obj.__call__() returns a context manager, such as one made by @contextmanager.
Rule of thumb: use __enter__/__exit__ for context-manager classes; use @contextmanager for short setup/teardown logic, especially factory-style helpers.
class Timer:
"""
Timer utility. Usage:
timer = Timer()
with timer("foo"):
do_something()
timer.tick("bar")
do_something_else()
timer.tock("bar")
timer.get_average_times() -> {"foo": 0.1, "bar": 0.2}
"""
def __init__(self):
self.reset()
@contextmanager
def __call__(self, key):
self.tick(key)
try:
yield None
finally:
self.tock(key)
def reset(self):
self.counts = defaultdict(int)
self.times = defaultdict(float)
self.start_times = {}
def tick(self, key):
if key in self.start_times:
raise ValueError(f"Timer is already ticking for key: {key}")
self.start_times[key] = time.time()
def tock(self, key):
if key not in self.start_times:
raise ValueError(f"Timer is not ticking for key: {key}")
self.counts[key] += 1
self.times[key] += time.time() - self.start_times[key]
del self.start_times[key]
def get_average_times(self, reset=True):
ret = {key: self.times[key] / self.counts[key] for key in self.counts}
if reset:
self.reset()
return ret
Key difference:
requires
obj.__enter__ / obj.__exit__.works if
obj.__call__()returns a context manager, such as one made by@contextmanager.Rule of thumb: use
__enter__/__exit__for context-manager classes; use@contextmanagerfor short setup/teardown logic, especially factory-style helpers.