Kids.cache
Kids python cache decorator and library.
Install / Use
/learn @0k/Kids.cacheREADME
========== kids.cache
.. image:: http://img.shields.io/pypi/v/kids.cache.svg?style=flat :target: https://pypi.python.org/pypi/kids.cache/ :alt: Latest PyPI version
.. image:: http://img.shields.io/pypi/dm/kids.cache.svg?style=flat :target: https://pypi.python.org/pypi/kids.cache/ :alt: Number of PyPI downloads
.. image:: http://img.shields.io/travis/0k/kids.cache/master.svg?style=flat :target: https://travis-ci.org/0k/kids.cache/ :alt: Travis CI build status
.. image:: http://img.shields.io/coveralls/0k/kids.cache/master.svg?style=flat :target: https://coveralls.io/r/0k/kids.cache :alt: Test coverage
kids.cache is a Python library providing a cache decorator.
It's part of 'Kids' (for Keep It Dead Simple) library. It has
no dependency to any python library.
Its main concern is to offer a very simple default usage scheme, without forgetting to offer full power inside when needed.
Maturity
This code is around ~100 lines of python, and it has a 100% test coverage.
However it still considered beta stage currently.
Compatibility
It is small and simple and should work anywhere.
To put it in longer details: the current code is simple enough that it use a common subset of python that is compatible with any platform on python 2.7 and python >= 3... and this without any specific modification.
Even then, You'll be happy to know that, this code is tested for compatibility at each commit with python 2.7, 3.4, 3.5, 3.6 on linux and windows platform.
Features
-
Use one simple call to
@cache, and a majority of all hidden complexity will vanish.- works out of the box everywhere you can stick a decorator (function, methods, property, classes...).
- support to be called before or after common decorators as
@property,@classmethod,@staticmethod.
-
With
@cacheseveral design pattern can be achieved:- memoization when used on function with arguments.
- lazy evaluation when placed on properties.
- singleton patterns when placed on classes.
-
Full customization at disposition:
-
cache clearing or cache stats functionality.
-
support of any cache store mecanism from
cachetools_ package. -
support of custom key function which allows:
- support of your exotic unhashable objects
- fine tune which function calls can be considered identic
- hand pick function dependencies in object (for method)
-
.. _cachetools: https://github.com/tkem/cachetools
Basic Usage
Function
This cache decorator is quite straightforward to use::
>>> from kids.cache import cache
>>> @cache
... def answer_to_everything():
... print("many insightfull calculation")
... return 42
Then the function answer_to_everything would only do the
calculation the first time called, and would save the result, and
directly return it the next calls::
>>> answer_to_everything()
many insightfull calculation
42
>>> answer_to_everything()
42
The body of the function was not executed anymore and the cache value was used.
It'll work with arguments::
>>> @cache
... def mysum(*args):
... print("calculating...")
... return sum(args)
>>> mysum(2, 2, 3)
calculating...
7
>>> mysum(1, 1, 1, 1)
calculating...
4
>>> mysum(2, 2, 3)
7
>>> mysum(1, 1, 1, 1)
4
And notice that by default, object are not typed, thus::
>>> mysum(1.0, 1, 1, 1)
4
Did trigger the cache, despite the first argument is a float and not an integer.
Methods
With methods::
>>> class MyObject(object):
... def __init__(self, a, b):
... self.a, self.b = a, b
...
... @cache
... def total(self):
... print("calculating...")
... return self.a + self.b
>>> xx = MyObject(2, 3)
>>> xx.total()
calculating...
5
>>> xx.total()
5
Cache is not shared between instances::
>>> yy = MyObject(2, 3)
>>> yy.total()
calculating...
5
Of course, if you change the inner values of the instance, this will NOT be detected by the caching method::
>>> xx.a = 5
>>> xx.total()
5
Look at advanced usages to see how to changes some of these behaviors.
Property
You can use the cache decorator with properties, and
provides a good way to have lazy evaluated attributes::
>>> class WithProperty(MyObject):
...
... @property
... @cache
... def total(self):
... print("evaluating...")
... return self.a + self.b
>>> xx = WithProperty(1, 1)
>>> xx.total
evaluating...
2
>>> xx.total
2
You can use @cache decorator before or after @property
decorator::
>>> class WithProperty(MyObject):
...
... @cache
... @property
... def total(self):
... print("evaluating...")
... return self.a + self.b
>>> xx = WithProperty(2, 2)
>>> xx.total
evaluating...
4
>>> xx.total
4
classmethod
You can use the cache decorator with classmethods, and
provides a good way to share cache between instances::
>>> class WithClassMethod(MyObject):
...
... a = 2
... b = 3
...
... @classmethod
... @cache
... def total(cls):
... print("evaluating...")
... return cls.a + cls.b
>>> WithClassMethod.total()
evaluating...
5
>>> WithClassMethod.total()
5
You can use @cache decorator before or after @property
decorator::
>>> class WithClassMethod(MyObject):
...
... a = 1
... b = 6
...
... @cache
... @classmethod
... def total(cls):
... print("evaluating...")
... return cls.a + cls.b
>>> WithClassMethod.total()
evaluating...
7
>>> WithClassMethod.total()
7
staticmethod
You can use the cache decorator with staticmethods::
>>> class WithStaticMethod(MyObject):
...
... @staticmethod
... @cache
... def total(a, b):
... print("evaluating...")
... return a + b
>>> WithStaticMethod.total(1, 3)
evaluating...
4
>>> WithStaticMethod.total(1, 3)
4
You can use @cache decorator before or after @property
decorator::
>>> class WithStaticMethod(MyObject):
...
... @cache
... @staticmethod
... def total(a, b):
... print("evaluating...")
... return a + b
>>> WithStaticMethod.total(2, 6)
evaluating...
8
>>> WithStaticMethod.total(2, 6)
8
class
Using cache with classes will allow variations around the
notion of singletons. A singleton shares the same id in memory,
so this shows a classical non-singleton behavior::
>>> a, b = object(), object()
>>> id(a) == id(b)
False
Factory based singleton
You can use the ``cache`` decorator with classes, effectively
implementing a factory pattern for creating singleton::
>>> @cache
... class MySingleton(MyObject):
... def __new__(cls):
... print("instanciating...")
... return MyObject.__new__(cls)
... def __init__(self):
... print("initializing...")
>>> a, b = MySingleton(), MySingleton()
instanciating...
initializing...
>>> id(a) == id(b)
True
Notice that both instance are the same object, so it was only
instanciated and initialized once.
But be warned: this is not anymore a class::
>>> MySingleton
<function MySingleton at ...>
Instanciation based singletons
Slightly different, the class singleton pattern can be achieved by
caching __new__::
>>> class MySingleton(MyObject):
... @cache
... def __new__(cls):
... print("instanciating...")
... return MyObject.__new__(cls)
... def __init__(self):
... print("initializing...")
>>> a, b = MySingleton(), MySingleton()
instanciating...
initializing...
initializing...
>>> id(a) == id(b)
True
Notice that both instance are the same object, so it was only
instanciated once. But the __init__ was called both times.
This is sometimes perfectly valid, but you might want to avoid this
also.
So if you don't want this, you should cache also __init__ method::
>>> class MySingleton(MyObject):
... @cache
... def __new__(cls):
... print("instanciating...")
... return MyObject.__new__(cls)
... @cache
... def __init__(self):
... print("initializing...")
>>> a, b = MySingleton(), MySingleton()
instanciating...
initializing...
>>> id(a) == id(b)
True
For both cases you'll keep your full object untouched of course::
>>> MySingleton
<class 'MySingleton'>
Singleton with arguments
Actually, these are only singletons if you call them successively with
the same arguments.
Or to be more precise, you can share your classes when their
instanciation's arguments are the same::
>>> @cache
... class MySingleton(MyObject):
... def __init__(self, a):
... self.a = a
... print("evaluating...")
>>> a, b = MySingleton(1), MySingleton(2)
evaluating...
evaluating...
>>> id(a) == id(b)
False
But::
>>> c = MySingleton(1)
>>> id(a) == id(c)
True
If you want a singleton that give you the same instance even if your
successive calls differs, you should check the advanced usage section
and the ``key`` argument.
Advanced Usage
==============
Most of the advanced usage implies to call the ``@cache`` decorator with
arguments. Please notice that::
>>> @cache
... def mysum1(*args):
... print("calculating...")
Related Skills
node-connect
349.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.9kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
