Skip to content

Commit

Permalink
Optimize cached_method when wrapping no-arg methods (#6381)
Browse files Browse the repository at this point in the history
Previously, the first time a no-arg cached method was called the
decorator would make three calls: `hasattr`, `object.__setattr__`,
`getattr`, and when called subsequently it would make two calls:
`hasattr`, `getattr`. Here we refactor the implementation to use a
sentinel value so that on the first call we only make two calls:
`getattr`, `object.__setattr__`, and when called subsequently we only
make a single `getattr` call.
  • Loading branch information
maffoo authored Dec 13, 2023
1 parent ec727ca commit 2f3c1e2
Showing 1 changed file with 9 additions and 3 deletions.
12 changes: 9 additions & 3 deletions cirq-core/cirq/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ def with_debug(value: bool) -> Iterator[None]:
from backports.cached_property import cached_property # type: ignore[no-redef]


# Sentinel used by wrapped_no_args below when method has not yet been cached.
_NOT_FOUND = object()


TFunc = TypeVar('TFunc', bound=Callable)


Expand Down Expand Up @@ -103,9 +107,11 @@ def decorator(func):

@functools.wraps(func)
def wrapped_no_args(self):
if not hasattr(self, cache_name):
object.__setattr__(self, cache_name, func(self))
return getattr(self, cache_name)
result = getattr(self, cache_name, _NOT_FOUND)
if result is _NOT_FOUND:
result = func(self)
object.__setattr__(self, cache_name, result)
return result

return wrapped_no_args

Expand Down

0 comments on commit 2f3c1e2

Please sign in to comment.