Waht do you think about my repeated_timer class

Chris Angelico rosuav at gmail.com
Wed Feb 2 17:58:46 EST 2022


On Thu, 3 Feb 2022 at 09:33, Barry <barry at barrys-emacs.org> wrote:
>
>
>
> > On 2 Feb 2022, at 21:12, Marco Sulla <Marco.Sulla.Python at gmail.com> wrote:
> >
> > You could add a __del__ that calls stop :)
>
> Didn’t python3 make this non deterministic when del is called?
>
> I thought the recommendation is to not rely on __del__ in python3 code.
>

The __del__ method is called when the object is being disposed of.
Aside from some considerations about interpreter shutdown, it's
perfectly reliable... as long as you understand that objects don't get
disposed of when they "go out of scope", but when they run out of
references and get garbage collected. So for instance, you can't
depend on "x = Spam(); x = Ham()" to dispose of the Spam object
instantly, because it might not get garbage collected instantly; but
you can depend on __del__ getting called when the Spam object gets
garbage collected.

With that said, I can guarantee you that a __del__ method is NOT the
right way to call stop, for one simple reason: the object will keep
its own references (via the timer) so long as it is running. So it
won't get garbage collected, and in fact, this is very important to it
being reliable. Consider this simpler example:

def call_soon(func):
    t = threading.Timer(10, func)
    t.start()

When this function returns, you can no longer refer to the object 't'.
Will the function be called at the appropriate time? Yes, it
absolutely will, and it would be highly surprising if it didn't! So
the thread itself keeps a reference to the important objects.

(Side point: The OP's code is quite inefficient, as it creates a new
thread for each reiteration, but there's nothing wrong with that if
you're looking for something simple.)

The main reason to "not rely on __del__" (and, by the way, that's
nothing to do with whether it's Python 2 or Python 3, this has been
true since early Py2 and possibly earlier) is that you don't know when
the object will be disposed of. So if you want to guarantee that
something is cleaned up, what you need is a way for the object itself
to still exist, but the corresponding resource to be cleaned up. A
classic example is a file object:

def read_then_write(fn):
    with open(fn) as read_file:
        data = read_file.read()
    print(read_file)
    # ... transform the data as required ...
    with open(fn, "w") as write_file:
        write_file.write(data)
    print(write_file)

After each 'with' block, the file object is still there. You can refer
to it. Nothing has destroyed the object. But the file has been closed,
guaranteeing that you can safely reopen it in a different mode
(regardless of your OS). The same could be done with this timer; an
__exit__ method would make a lot of sense here, and would allow the
timer to be used in a with block to govern its execution. (It also
isn't really necessary, but if you want a good Pythonic way to show
the beginning and end of its use area, a 'with' block is the way to
go.)

ChrisA


More information about the Python-list mailing list