| Name | memoiz JSON |
| Version |
1.2.0
JSON |
| download |
| home_page | None |
| Summary | Memoiz is a memoization decorator that makes reasonable assumptions about how and if to cache the return value of a function or method based on the arguments passed to it. The decorator can be used on both free and bound functions. |
| upload_time | 2024-10-19 19:46:38 |
| maintainer | None |
| docs_url | None |
| author | None |
| requires_python | >=3.10 |
| license | None |
| keywords |
|
| VCS |
 |
| bugtrack_url |
|
| requirements |
No requirements were recorded.
|
| Travis-CI |
No Travis.
|
| coveralls test coverage |
No coveralls.
|
# Memoiz
A thread-safe memoization decorator for functions and methods.
## Introduction
Memoiz provides a function decorator that can be used in order to augment functions or methods with memoization capabilties. It makes reasonable assumptions about how and if to cache the return value of a function or method based on the arguments passed to it. The decorator can be used on both free and bound functions.
## Features
- A thread-safe cache
- Use the Memoiz decorator on free and bound functions
- Support for parameter and return type hints
- Cache invalidation
## Table of Contents
- [Installation](#installation)
- [Usage](#usage)
- [Bound Functions (methods)](#bound-functions-methods)
- [Free Functions](#free-functions)
- [Limitations](#limitations)
- [API](#api)
- [Test](#test)
## Installation
```bash
pip install memoiz
```
## Usage
### Bound Functions (methods)
In this example you will use Memoiz to memoize the return value of the `greeter.greet` method and print the greeting.
```py
from memoiz import Memoiz
# `cache` is a Python decorator and a callable.
cache = Memoiz()
class Greeter:
def __init__(self):
self.adv = "Very"
@cache # Use the `cache` decorator in order to add memoization capabilities to the `greet` method.
def greet(self, adj: str) -> str:
return f"Hello, {self.adv} {adj} World!"
greeter = Greeter()
print("1:", cache._cache)
greeting = greeter.greet("Happy")
print("2:", greeting)
```
```bash
1: {}
2: Hello, Very Happy World!
```
As a continuation of the example, you will selectively invalidate cached articles using the `cache.invalidate` method.
```python
greeter = Greeter()
print("1:", cache._cache)
greeting = greeter.greet("Happy")
print("2:", greeting)
greeting = greeter.greet("Cautious")
print("3:", greeting)
# The cache has memoized the two method calls.
print("4:", cache._cache)
# Invalidate the call to `greeter.greet` with the "Happy" argument.
# ⮶ instance
cache.invalidate(greeter.greet, greeter, "Happy")
# ⮴ method ⮴ args
print("5:", cache._cache)
# Invalidate the call to `greeter.greet` with the `Cautious` argument.
cache.invalidate(greeter.greet, greeter, "Cautious")
# The cache is empty.
print("6:", cache._cache)
```
```bash
1: {}
2: Hello, Very Happy World!
3: Hello, Very Cautious World!
4: {<bound method Greeter.greet of <__main__.Greeter object at 0x7fa5e7f837f0>>: {((<__main__.Greeter object at 0x7fa5e7f837f0>, 'Happy'), ()): 'Hello, Very Happy World!', ((<__main__.Greeter object at 0x7fa5e7f837f0>, 'Cautious'), ()): 'Hello, Very Cautious World!'}}
5: {<bound method Greeter.greet of <__main__.Greeter object at 0x7fa5e7f837f0>>: {((<__main__.Greeter object at 0x7fa5e7f837f0>, 'Cautious'), ()): 'Hello, Very Cautious World!'}}
6: {}
```
### Free Functions
In this example you will use Memoiz to memoize the return value of the `greet` function and print the greeting.
```py
from memoiz import Memoiz
cache = Memoiz()
@cache
def greet(adj: str) -> str:
return f"Hello, {adj} World!"
print("1:", cache._cache)
greeting = greet("Happy")
print("2:", greeting)
```
```bash
1: {}
2: Hello, Happy World!
```
As a continuation of the example, you will selectively invalidate cached articles using the `cache.invalidate` method.
```python
print("1:", cache._cache)
greeting = greet("Happy")
print("2:", greeting)
greeting = greet("Cautious")
print("3:", greeting)
print("4:", cache._cache)
# ⮶ args
cache.invalidate(greet, "Happy")
# ⮴ function
# The call using the "Happy" argument is deleted; however, the call using the
# "Cautious" is still present.
print("5:", cache._cache)
# ⮶ args
cache.invalidate(greet, "Cautious")
# ⮴ function
# The cache is now empty.
print("6:", cache._cache)
```
```bash
1: {}
2: Hello, Happy World!
3: Hello, Cautious World!
4: {<function greet at 0x7fa5cefb8430>: {(('Happy',), ()): 'Hello, Happy World!', (('Cautious',), ()): 'Hello, Cautious World!'}}
5: {<function greet at 0x7fa5cefb8430>: {(('Cautious',), ()): 'Hello, Cautious World!'}}
6: {}
```
## Limitations
Memoization relies on the behavior of pure functions; given the same input the function produces the same output. It isn't safe to assume that a callable is pure in Python; hence, discretion must be used when applying the decorator to a given callable.
Memoiz uses a Python dictionary in order to cache the arguments and respective return value of the callable. Memoiz will attempt to transform a callable's arguments into a hashable representation. If it succeeds, the hashable representation of the callable's arguments is used as the dictionary key in order to store and look up the cached return value. If it fails, Memoiz will call the decorated function or method and return the result.
Memoiz employs a few strategies to produce a hashable lookup key. Memoiz will recursively iterate through `dict`, `list`, `set`, and `tuple` type arguments, transforming mutable objects into hashable representations. See the [Type Transformation](#type-transformation) table for type transformations. When a primitive is encountered (e.g., `int`, `float`, `complex`, `bool`, `str`, `None`), it is left as is. If `allow_hash` is set to `True` (the default), Memoiz will additionally attempt to discern if an object is hashable using Python's `hash` function.
Effectively what this all means is that if you are using common Python iterables and primitives as arguments to your callable, and if your callable doesn't have side effects, Memoiz should be able to accurately apply memoization to your callable.
### Type Transformation
| Type | Hashable Type |
| ----- | --------------- |
| dict | tuple of tuples |
| list | tuple |
| tuple | tuple |
| set | tuple |
## API
### The Memoiz Class
**memoiz.Memoiz(immutables, sequentials, allow_hash, deep_copy)**
- immutables `Tuple[type, ...]` An optional tuple of types that are assumed to be immutable. **Default:** `(int, float, complex, bool, str, type(None))`
- sequentials `Tuple[type, ...]` An optional tuple of types that are assumed to be sequence-like. **Default** `(list, tuple, set)`
- mapables `Tuple[type, ...]` An optional tuple of types that are assumed to be dict-like. **Default** `(dict,)`
- allow_hash `bool` An optional flag that indicates if an object's hash is sufficient for indexing the callable's arguments. **Default:** `True`
- deep_copy `bool` Optionally return the cached return value using Python's `copy.deepcopy`. This can help prevent mutations of the cached return value. **Default:** `True`.
**memoiz.\_\_call\_\_(callable)**
- callable `typing.Callable` The function or method for which you want to add memoization.
A `Memoiz` instance ([see above](#the-cache-class)) is a callable. This is the `@cache` decorator that is used in order to add memoization to a callable. Please see the above [usage](#usage) for how to use this decorator.
**memoiz.invalidate(callable, \*args, \*\*kwargs)**
- callable `typing.Callable` The callable.
- args `Any` The arguments passed to the callable.
- kwargs `Any` The keyword arguments passed to the callable.
Invalidates the cache for the specified callable and arguments. See the [usage](#usage) for for how to invalidate the cache.
> **NB** The first argument of a method (i.e., a bound function) is the object instance e.g., the `self` in the method definition.
**memoiz.invalidate_all()**
Resets the cache making items in the old cache potentially eligible for garbage collection.
## Test
Clone the repository.
```bash
git clone https://github.com/faranalytics/memoiz.git
```
Change directory into the root of the repository.
```bash
cd memoiz
```
Run the tests.
```bash
python tests/test.py -v
```
Raw data
{
"_id": null,
"home_page": null,
"name": "memoiz",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": null,
"author": null,
"author_email": "Adam Patterson <adam@farar.net>",
"download_url": "https://files.pythonhosted.org/packages/7a/e0/b8608c2731d51da2661ed41690d3701d9ecefa135310a16d7a372aa5ba95/memoiz-1.2.0.tar.gz",
"platform": null,
"description": "# Memoiz\n\nA thread-safe memoization decorator for functions and methods.\n\n## Introduction\n\nMemoiz provides a function decorator that can be used in order to augment functions or methods with memoization capabilties. It makes reasonable assumptions about how and if to cache the return value of a function or method based on the arguments passed to it. The decorator can be used on both free and bound functions.\n\n## Features\n\n- A thread-safe cache\n- Use the Memoiz decorator on free and bound functions\n- Support for parameter and return type hints\n- Cache invalidation\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n - [Bound Functions (methods)](#bound-functions-methods)\n - [Free Functions](#free-functions)\n- [Limitations](#limitations)\n- [API](#api)\n- [Test](#test)\n\n## Installation\n\n```bash\npip install memoiz\n```\n\n## Usage\n\n### Bound Functions (methods)\n\nIn this example you will use Memoiz to memoize the return value of the `greeter.greet` method and print the greeting.\n\n```py\nfrom memoiz import Memoiz\n\n# `cache` is a Python decorator and a callable.\ncache = Memoiz()\n\n\nclass Greeter:\n\n def __init__(self):\n self.adv = \"Very\"\n\n @cache # Use the `cache` decorator in order to add memoization capabilities to the `greet` method.\n def greet(self, adj: str) -> str:\n return f\"Hello, {self.adv} {adj} World!\"\n\n\ngreeter = Greeter()\n\nprint(\"1:\", cache._cache)\n\ngreeting = greeter.greet(\"Happy\")\n\nprint(\"2:\", greeting)\n```\n\n```bash\n1: {}\n2: Hello, Very Happy World!\n```\n\nAs a continuation of the example, you will selectively invalidate cached articles using the `cache.invalidate` method.\n\n```python\ngreeter = Greeter()\n\nprint(\"1:\", cache._cache)\n\ngreeting = greeter.greet(\"Happy\")\n\nprint(\"2:\", greeting)\n\ngreeting = greeter.greet(\"Cautious\")\n\nprint(\"3:\", greeting)\n\n# The cache has memoized the two method calls.\nprint(\"4:\", cache._cache)\n\n# Invalidate the call to `greeter.greet` with the \"Happy\" argument.\n# \u2bb6 instance\ncache.invalidate(greeter.greet, greeter, \"Happy\")\n# \u2bb4 method \u2bb4 args\n\nprint(\"5:\", cache._cache)\n\n# Invalidate the call to `greeter.greet` with the `Cautious` argument.\ncache.invalidate(greeter.greet, greeter, \"Cautious\")\n\n# The cache is empty.\nprint(\"6:\", cache._cache)\n```\n\n```bash\n1: {}\n2: Hello, Very Happy World!\n3: Hello, Very Cautious World!\n4: {<bound method Greeter.greet of <__main__.Greeter object at 0x7fa5e7f837f0>>: {((<__main__.Greeter object at 0x7fa5e7f837f0>, 'Happy'), ()): 'Hello, Very Happy World!', ((<__main__.Greeter object at 0x7fa5e7f837f0>, 'Cautious'), ()): 'Hello, Very Cautious World!'}}\n5: {<bound method Greeter.greet of <__main__.Greeter object at 0x7fa5e7f837f0>>: {((<__main__.Greeter object at 0x7fa5e7f837f0>, 'Cautious'), ()): 'Hello, Very Cautious World!'}}\n6: {}\n```\n\n### Free Functions\n\nIn this example you will use Memoiz to memoize the return value of the `greet` function and print the greeting.\n\n```py\nfrom memoiz import Memoiz\n\ncache = Memoiz()\n\n\n@cache\ndef greet(adj: str) -> str:\n return f\"Hello, {adj} World!\"\n\n\nprint(\"1:\", cache._cache)\n\ngreeting = greet(\"Happy\")\n\nprint(\"2:\", greeting)\n```\n\n```bash\n1: {}\n2: Hello, Happy World!\n```\n\nAs a continuation of the example, you will selectively invalidate cached articles using the `cache.invalidate` method.\n\n```python\nprint(\"1:\", cache._cache)\n\ngreeting = greet(\"Happy\")\n\nprint(\"2:\", greeting)\n\ngreeting = greet(\"Cautious\")\n\nprint(\"3:\", greeting)\n\nprint(\"4:\", cache._cache)\n\n# \u2bb6 args\ncache.invalidate(greet, \"Happy\")\n# \u2bb4 function\n\n# The call using the \"Happy\" argument is deleted; however, the call using the\n# \"Cautious\" is still present.\nprint(\"5:\", cache._cache)\n\n# \u2bb6 args\ncache.invalidate(greet, \"Cautious\")\n# \u2bb4 function\n\n# The cache is now empty.\nprint(\"6:\", cache._cache)\n```\n\n```bash\n1: {}\n2: Hello, Happy World!\n3: Hello, Cautious World!\n4: {<function greet at 0x7fa5cefb8430>: {(('Happy',), ()): 'Hello, Happy World!', (('Cautious',), ()): 'Hello, Cautious World!'}}\n5: {<function greet at 0x7fa5cefb8430>: {(('Cautious',), ()): 'Hello, Cautious World!'}}\n6: {}\n```\n\n## Limitations\n\nMemoization relies on the behavior of pure functions; given the same input the function produces the same output. It isn't safe to assume that a callable is pure in Python; hence, discretion must be used when applying the decorator to a given callable.\n\nMemoiz uses a Python dictionary in order to cache the arguments and respective return value of the callable. Memoiz will attempt to transform a callable's arguments into a hashable representation. If it succeeds, the hashable representation of the callable's arguments is used as the dictionary key in order to store and look up the cached return value. If it fails, Memoiz will call the decorated function or method and return the result.\n\nMemoiz employs a few strategies to produce a hashable lookup key. Memoiz will recursively iterate through `dict`, `list`, `set`, and `tuple` type arguments, transforming mutable objects into hashable representations. See the [Type Transformation](#type-transformation) table for type transformations. When a primitive is encountered (e.g., `int`, `float`, `complex`, `bool`, `str`, `None`), it is left as is. If `allow_hash` is set to `True` (the default), Memoiz will additionally attempt to discern if an object is hashable using Python's `hash` function.\n\nEffectively what this all means is that if you are using common Python iterables and primitives as arguments to your callable, and if your callable doesn't have side effects, Memoiz should be able to accurately apply memoization to your callable.\n\n### Type Transformation\n\n| Type | Hashable Type |\n| ----- | --------------- |\n| dict | tuple of tuples |\n| list | tuple |\n| tuple | tuple |\n| set | tuple |\n\n## API\n\n### The Memoiz Class\n\n**memoiz.Memoiz(immutables, sequentials, allow_hash, deep_copy)**\n\n- immutables `Tuple[type, ...]` An optional tuple of types that are assumed to be immutable. **Default:** `(int, float, complex, bool, str, type(None))`\n- sequentials `Tuple[type, ...]` An optional tuple of types that are assumed to be sequence-like. **Default** `(list, tuple, set)`\n- mapables `Tuple[type, ...]` An optional tuple of types that are assumed to be dict-like. **Default** `(dict,)`\n- allow_hash `bool` An optional flag that indicates if an object's hash is sufficient for indexing the callable's arguments. **Default:** `True`\n- deep_copy `bool` Optionally return the cached return value using Python's `copy.deepcopy`. This can help prevent mutations of the cached return value. **Default:** `True`.\n\n**memoiz.\\_\\_call\\_\\_(callable)**\n\n- callable `typing.Callable` The function or method for which you want to add memoization.\n\nA `Memoiz` instance ([see above](#the-cache-class)) is a callable. This is the `@cache` decorator that is used in order to add memoization to a callable. Please see the above [usage](#usage) for how to use this decorator.\n\n**memoiz.invalidate(callable, \\*args, \\*\\*kwargs)**\n\n- callable `typing.Callable` The callable.\n- args `Any` The arguments passed to the callable.\n- kwargs `Any` The keyword arguments passed to the callable.\n\nInvalidates the cache for the specified callable and arguments. See the [usage](#usage) for for how to invalidate the cache.\n\n> **NB** The first argument of a method (i.e., a bound function) is the object instance e.g., the `self` in the method definition.\n\n**memoiz.invalidate_all()**\n\nResets the cache making items in the old cache potentially eligible for garbage collection.\n\n## Test\n\nClone the repository.\n```bash\ngit clone https://github.com/faranalytics/memoiz.git\n```\nChange directory into the root of the repository.\n```bash\ncd memoiz\n```\nRun the tests.\n```bash\npython tests/test.py -v\n```",
"bugtrack_url": null,
"license": null,
"summary": "Memoiz is a memoization decorator that makes reasonable assumptions about how and if to cache the return value of a function or method based on the arguments passed to it. The decorator can be used on both free and bound functions.",
"version": "1.2.0",
"project_urls": {
"Bug Tracker": "https://github.com/faranalytics/memoiz/issues",
"Homepage": "https://github.com/faranalytics/memoiz.git"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "b958f66a0855bcbd2ab30003f8470f39ce93a858d102b65d4c6465cc7cd3be53",
"md5": "f1990f0460cd81cb5ef0cbdeb1ef99d4",
"sha256": "f48dcac7293375abbfd3f43e8ce8d192e2ee4af2f3c04010874d05a70ce76bda"
},
"downloads": -1,
"filename": "memoiz-1.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f1990f0460cd81cb5ef0cbdeb1ef99d4",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 6230,
"upload_time": "2024-10-19T19:46:39",
"upload_time_iso_8601": "2024-10-19T19:46:39.619898Z",
"url": "https://files.pythonhosted.org/packages/b9/58/f66a0855bcbd2ab30003f8470f39ce93a858d102b65d4c6465cc7cd3be53/memoiz-1.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "7ae0b8608c2731d51da2661ed41690d3701d9ecefa135310a16d7a372aa5ba95",
"md5": "6badab3ac45aa418bdcedd35506b255e",
"sha256": "4c603aa10ac59d0a7750b61035ae1b0ca7ae09e19b2b62094d09ff4bfa5656d2"
},
"downloads": -1,
"filename": "memoiz-1.2.0.tar.gz",
"has_sig": false,
"md5_digest": "6badab3ac45aa418bdcedd35506b255e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 7778,
"upload_time": "2024-10-19T19:46:38",
"upload_time_iso_8601": "2024-10-19T19:46:38.296346Z",
"url": "https://files.pythonhosted.org/packages/7a/e0/b8608c2731d51da2661ed41690d3701d9ecefa135310a16d7a372aa5ba95/memoiz-1.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-19 19:46:38",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "faranalytics",
"github_project": "memoiz",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "memoiz"
}