Coverage for pycommons / ds / cache.py: 100%
18 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 03:04 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 03:04 +0000
1"""A factory for functions checking whether argument values are new."""
2from typing import Callable, Final, TypeVar
5def str_is_new() -> Callable[[str], bool]:
6 """
7 Create a function returning `True` when seeing new `str` values.
9 Creates a function which returns `True` only the first time it receives a
10 given string argument and `False` all subsequent times.
11 This is based on https://stackoverflow.com/questions/27427067
13 :returns: a function `str_is_new(xx)` that will return `True` the first
14 time it encounters any value `xx` and `False` for all values it has
15 already seen
17 >>> check = str_is_new()
18 >>> print(check("a"))
19 True
20 >>> print(check("a"))
21 False
22 >>> print(check("b"))
23 True
24 >>> print(check("b"))
25 False
26 """
27 setdefault: Final[Callable] = {}.setdefault
28 n = 0 # noqa
30 def __add(x) -> bool:
31 nonlocal n
32 n += 1
33 return setdefault(x, n) == n
35 return __add
38#: a type variable for the representation-base cache
39#: :func:`pycommons.ds.cache.repr_cache`.
40T = TypeVar("T")
43def repr_cache() -> Callable[[T], T]:
44 """
45 Create a cache based on the string representation of an object.
47 In this type of cache is that the `repr`-representations of objects
48 are used as keys. The first time an object with a given representation is
49 encountered, it is stored in the cache and returned. The next time an
50 object with the same representation is put into this method, the original
51 object with that representation is returned instead.
53 This can be used to still cache and canonically retrieve objects which by
54 themselves are not hashable, like numpy arrays. While the cache itself is
55 not memory-friendly, it can be used to build data structures that re-use
56 the same objects again and again. If these data structures are heavily
57 used, then this can improve the hardware-cache-friendliness of the
58 corresponding code.
60 :return: the cache function
61 :raises TypeError: if the type of a cached object is incompatible with the
62 type of a requested object
64 >>> cache: Callable[[object], object] = repr_cache()
66 >>> a = f"{1 * 5}"
67 >>> b = "5"
68 >>> a is b
69 False
71 >>> cache(a)
72 '5'
73 >>> cache(b) is a
74 True
76 >>> x = 5.78
77 >>> y = 578 / 100
78 >>> y is x
79 False
81 >>> cache(y)
82 5.78
83 >>> cache(x) is y
84 True
86 >>> class Dummy:
87 ... def __repr__(self):
88 ... return "22222"
89 >>> _ = cache(Dummy())
90 >>> try:
91 ... cache(22222)
92 ... except TypeError as te:
93 ... s = str(te)
94 >>> print(s[:34])
95 Cache yields element of wrong type
96 >>> "Dummy" in s
97 True
98 >>> "int" in s
99 True
100 """
101 setdefault: Final[Callable] = {}.setdefault
103 def __add(x: T) -> T:
104 z: Final[T] = setdefault(repr(x), x)
105 tpe = type(x)
106 if not isinstance(z, tpe):
107 raise TypeError("Cache yields element of wrong type "
108 f"{type(z)}, should be {tpe}.")
109 return z
111 return __add