下面是一个简单的代码示例,可能有助于解释我的问题。
- 文件_1.py**
from functools import lru_cache
from file_2 import add_stuff, add_stats
@lru_cache()
def add(x, y):
return x + y
if __name__ == "__main__":
add(1, 2)
add(1, 2)
add(3, 4)
print(add.cache_info)
print(add.cache_info())
add_stuff(1, 2)
add_stuff(3, 4)
add_stats()
- 文件_2.py**
def add_stuff(x, y):
from file_1 import add
add(x, y)
def add_stats():
from file_1 import add
print(add.cache_info)
print(add.cache_info())
输出如下所示:
<built-in method cache_info of functools._lru_cache_wrapper object at 0x017E9E48>
CacheInfo(hits=1, misses=2, maxsize=128, currsize=2)
<built-in method cache_info of functools._lru_cache_wrapper object at 0x017E9D40>
CacheInfo(hits=0, misses=2, maxsize=128, currsize=2)
当我在定义函数的文件中使用函数对象时,函数对象与其他文件导入它时的对象是不同的,这意味着对于lru_cache这样的东西,如果你没有意识到这一点,如果你不将缓存的函数保存在与使用它们的文件不同的文件中,你可能会在你的进程/线程中填充两个缓存。
我的问题是,这是一个python让你注意的吗?或者是有文档在哪里,我只是从来没有读过,更深入地解释这一点?我看了lru_cache文档,这是没有调用那里作为任何需要注意的。
1条答案
按热度按时间mftmpeh81#
当我在定义函数的文件中使用函数时,函数对象与另一个文件导入它时的对象不同。
是的,这里有两个独立的缓存,因为每个缓存都修饰了一个独立的函数对象,之所以有两个独立的函数对象,是因为有两个独立的模块是从同一个源代码创建的。
其中一个模块是由
from file_1 import add
创建的,这导致一个模块缓存在sys.modules
中,其键为'file_1'
,__name__
属性为file_1
(以后使用import
时将该高速缓存中查找此模块)。另一个是通过运行
file_1.py
作为主脚本创建的,这将创建一个__name__
属性为__main__
的模块。这就是
if __name__ == '__main__':
技巧工作的原因和方式。模块可用的全局变量-即通过使用globals()
获得的内容-来自模块对象的属性**。顶层脚本也用模块对象表示-它们只是不使用import
导入(尽管它们是使用大部分相同的机制创建的,并且被缓存;'__main__'
键将出现在sys.modules
中)。这就是信息的来源,因此__name__
在正常情况下作为全局变量存在。这是一个python需要注意的地方吗?或者有文档在哪里,我只是从来没有读过,更深入地解释这一点?我看了lru_cache文档,这是没有调用那里作为任何需要注意的事情。
lru_cache
文档中没有解释,因为这不是lru_cache
的错,任何装饰器都会发生这种情况,事实上,任何使函数对象的独立标识相关的代码都会发生这种情况,例如,如果我们创建module_example.py
:(The
__name__
的再次出现应该会使正在发生的事情变得显而易见-当然,我们甚至可以直接在函数中使用__name__
)现在我们在交互模式下测试代码-运行它,使用全局函数,然后导入模块并使用导入的函数:
这只是一个陷阱,因为期望一个模块既能作为顶层代码工作,又能作为可导入的东西工作,这强加了一些设计考虑。通常,如果代码打算导入,“驱动程序”代码块(如果有的话)只会做一个非正式的测试;或者为模块的功能提供一个简单的一次性UI,它 * 不关心 * 与相同代码的导入模块版本的一致性。
换句话说:这里真实的的问题是循环导入。
file_1
间接地导入自身以获得其自身的功能,并且它仅“工作”,因为第一次将模块隐式重命名为__main__
。