It's sad the difference between expiry and eviction isn't explored more fully. There are plenty of posts describing cache eviction strategies that barely touch on expiry issues, and a handful of posts about cache expiry that barely touch on eviction.
Regarding the decorators, there's a reason it's popular: it saves boilerplate. The good news is that it's python, so you can do something like this:
from pyappcache import RedisCache
@RedisCache
def get_slow_thing_v3(thing_id):
thing = get_slow_thing_from_a_database_layer()
return thing
def update_something_slow(thing_id, new_thing):
get_slow_thing_v3.set_cache(thing_id, new_thing)
set_thing_in_a_database_layer(new_thing)