英文:
Does it make sense to decorate a @staticmethod with an @lru_cache?
问题
在Python3中,我有以下代码:
class MyClass:
@lru_cache(maxsize=None)
@staticmethod
def run_expensive_computation() -> bool:
return expensive_function("hard_coded_string")
假设:
-
expensive_function() 本身没有缓存
-
"hard_coded_string" 永远不会更改,因此不需要将其作为
run_expensive_computation
的输入
如果 run_expensive_computation
不接受任何输入,是否在此处使用 @lru_cache(maxsize=None)
是不是浪费时间?在这种情况下,缓存中保存的键是什么?
英文:
I have in python3:
class MyClass:
@lru_cache(maxsize=None)
@staticmethod
def run_expensive_computation() -> bool:
return expensive_function("hard_coded_string")
Assuming:
-
expensive_function() itself is NOT cached
-
"hard_coded_string" will never change so no need to make that as
input torun_expensive_computation
Is it a waste of time to put @lru_cache(maxsize=None)
here if run_expensive_computation
takes no inputs ? What will be the key saved in the cache in this case ?
答案1
得分: 0
不,不行。它是有问题的 - 在Python版本小于3.10的情况下,你根本无法定义这样的类,而在Python版本大于等于3.10的情况下,这会破坏staticmethod - 它将不再能够从实例中调用。
不过,将一个被缓存的方法与staticmethod修饰符结合使用是有意义的,例如:
class MyClass:
@staticmethod
@lru_cache(maxsize=None)
def run_expensive_computation() -> bool:
return expensive_function("hard_coded_string")
英文:
> Does it make sense to decorate a @staticmethod
with an @lru_cache
?
No, it does not. It's bugged/broken - on Python < 3.10 you can not define a class like this at all, and on Python >= 3.10 it will break the staticmethod to do so - it will no longer be callable from an instance.
It does make sense to decorate a cached method with staticmethod, though:
class MyClass:
@staticmethod
@lru_cache(maxsize=None)
def run_expensive_computation() -> bool:
return expensive_function("hard_coded_string")
答案2
得分: 0
lru_cache
给这个调用增加了不必要的开销,对于这样一个简单的情况来说并不需要。相反,你可以有一个锁来保护你自己的缓存变量。如果它是 None
,那么在锁内部执行昂贵的操作。否则,返回缓存。在第一次调用之后,成本只是一个对 None
的测试。
import threading
class MyClass:
cache_lock = threading.Lock()
cache = None
@staticmethod
def run_expensive_computation() -> bool:
if MyClass.cache is None:
with MyClass.cache_lock:
if MyClass.cache is None:
MyClass.cache = expensive_function("hard_coded_string")
return MyClass.cache
英文:
lru_cache
adds overhead to the call that isn't needed for such a simple case. Instead, you could have a lock that protects your own cache variable. If its None
, you do the expensive work in the lock. Otherwise, return the cache. After the first call, the cost is simply a test for None
.
import threading
class MyClass:
cache_lock = threading.Lock()
cache = None
@staticmethod
def run_expensive_computation() -> bool:
if MyClass.cache is None:
with MyClass.cache_lock:
if MyClass.cache is None:
MyClass.cache = expensive_function("hard_coded_string")
retrun MyClass.cache
答案3
得分: -1
这是可以的,lru_cache
很好地处理了这种情况。
一个包装器被使用,捕获参数作为 *args, **kwargs
,然后从中提取一个键。在这种情况下,它只是一个空元组,如果你想查看详细信息,只需查看源代码。那是Python版本,尽管CPython实际上会使用这里找到的C实现。
另外,在最近的Python版本中,>= 3.9,你可以使用 functools.cache
来处理 maxsize=None
的情况,这应该比LRU缓存更小更快。
注意:在Python版本小于3.10的情况下,无法以这个顺序在staticmethod
对象上使用functools
的缓存装饰器。不过,装饰器的顺序可以颠倒,无论版本如何都应该能工作。
英文:
Sure, this is fine and the lru_cache
handles this case fine.
A wrapper is used that catches the arguments as *args, **kwargs
and a key is extracted from that. In this case, it is just an empty tuple, if you want to see the details, just look at the source. That is the Python version, although, CPython will actually use a C implementation found here
As an aside, in the most recent python versions, >= 3.9, you can use functools.cache
for the case where you use maxsize=None
, which should be smaller and faster than the LRU cache.
Note: the functools
caching decorators can not be used in this order with staticmethod
objects on versions of Python prior to 3.10
. The order of the decorators could be reversed though, and that should work regardless of version.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论