How should a clock object be made in Python?
Question: I'm making a very simple clock object in Python. I want to be able to
instantiate a clock giving it a name and have it record the time it is running
for, with pause functionality. I think I've nearly got it, but the pause
functionality is giving me some trouble -- the clock continues to accumulate
time even when stopped. I think the problem lies in the way the accumulated
run time (the `_runTime` data attribute) is updated (in the method
`update()`). I would welcome assistance in getting this code working sensibly
and would welcome any other comments on making the ideas here better.

    
    
    import time
    import datetime
    
    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'))
    
    class Clock(object):
    
        def __init__(
            self,
            name  = None,
            start = True
            ):
            # options
            self._name      = name
            self._start     = start # Boolean start clock on instantiation
            # internal
            self._startTime = None
            self._stopTime  = None
            self._runTime   = None
            self._running   = False
            # If a global clock list is detected, add a clock instance to it.
            if "clocks" in globals():
                clocks.add(self)
            if self._start:
                self.start()
    
        def name(
            self
            ):
            return(self._name)
    
        def start(
            self
            ):
            self._running   = True
            self._startTime = datetime.datetime.utcnow()
    
        def stop(
            self
            ):
            self._stopTime = datetime.datetime.utcnow()
            self._running  = False
            self.update()
    
        def startTime(
            self
            ):
            return(style_datetime_object(datetimeObject = self._startTime))
    
        def update(
            self
            ):
            # If the clock is running, the run time is the difference between the
            # current time and the start time (added to any previously accumulated
            # run time). If the clock is not running, the run time is the difference
            # between the stop time and the start time (added to any previously
            # accumulated run time).
            if self._running:
                if self._runTime:
                    self._runTime = self._runTime + datetime.datetime.utcnow() - self._startTime
                else:
                    self._runTime = datetime.datetime.utcnow() - self._startTime
            else:
                if self._runTime:
                    self._runTime = self._runTime + self._stopTime - self._startTime
                else:
                    self._runTime = self._stopTime - self._startTime
    
        def time(
            self
            ):
            self.update()
            return(self._runTime.total_seconds())
    
    print("create clock")
    a = Clock(name = "hello")
    print("clock start time: {time}".format(time = a.startTime()))
    print("sleep 2 seconds")
    time.sleep(2)
    print("clock current time (s): {time}".format(time = a.time()))
    
    print
    
    print("create new clock")
    b = Clock(name = "world")
    print("clock start time: {time}".format(time = b.startTime()))
    print("sleep 2 seconds")
    time.sleep(2)
    print("clock current time (s): {time}".format(time = b.time()))
    print("stop clock")
    b.stop()
    print("sleep 2 seconds")
    time.sleep(2)
    print("clock current time (s): {time}".format(time = b.time()))
    


Answer: I found your code hard to follow. Here is my (much smaller) clock anyway, may
be you can draw some inspiration to implement any lacking feature later.

    
    
    import datetime
    
    class Clock(object):
        def __init__(self):
            self.reset()
        def reset(self):
            self.accumulator = datetime.timedelta(0)
            self.started = None
        def start_stop(self):
            if self.started:
                self.accumulator += (
                    datetime.datetime.utcnow() - self.started
                )
                self.started = None
            else:
                self.started = datetime.datetime.utcnow()
        @property
        def elapsed(self):
            if self.started:
                return self.accumulator + (
                    datetime.datetime.utcnow() - self.started
                )
            return self.accumulator
        def __repr__(self):
            return "<Clock {} ({})>".format(
                self.elapsed,
                'started' if self.started else 'stopped'
            )
    

Tests:

    
    
    c = Clock()
    print c
    print "Starting..."; c.start_stop()
    for i in range(4):
        time.sleep(2)
        print c
    print "Stopping..."; c.start_stop()
    for i in range(4):
        time.sleep(2)
        print c
    print "Starting..."; c.start_stop()
    for i in range(4):
        time.sleep(2)
        print c
    print "Resetting..."; c.reset()
    print c
    

Result:

    
    
    <Clock 0:00:00 (stopped)>
    Starting...
    <Clock 0:00:02.002085 (started)>
    <Clock 0:00:04.004263 (started)>
    <Clock 0:00:06.006483 (started)>
    <Clock 0:00:08.007675 (started)>
    Stopping...
    <Clock 0:00:08.007756 (stopped)>
    <Clock 0:00:08.007756 (stopped)>
    <Clock 0:00:08.007756 (stopped)>
    <Clock 0:00:08.007756 (stopped)>
    Starting...
    <Clock 0:00:10.009876 (started)>
    <Clock 0:00:12.012034 (started)>
    <Clock 0:00:14.013143 (started)>
    <Clock 0:00:16.015291 (started)>
    Resetting...
    <Clock 0:00:00 (stopped)>
    

