我的问题与this one非常相似,但在我的情况下,接受的答案并没有在包中使用时装饰包中的所有功能,而且我不确定原因。
例如,我有一个像这样设置的项目:
project/
package/
__init__.py
module_a.py
module_b.py
main.py
__init__.py
from .module_a import *
from .module_b import *
import types
# This is the decorator that will be used
from functools import lru_cache
for name, obj in list(globals().items()):
if isinstance(obj, types.FunctionType):
globals()[name] = lru_cache(maxsize=100)(obj)
module_a.py
from .module_b import b
def a(arg):
return b
module_b.py
def b(arg):
return arg
main.py
import package
print(package.a.cache_info())
print(package.a(None).cache_info())
print(package.b.cache_info())
导入包时,__init__.py
在单步执行代码时装饰globals
中的函数就好了。但是,如果我执行main.py
,我会收到以下错误:
C:\Users\pbreach\Anaconda3\python.exe C:/Users/pbreach/PycharmProjects/project/main.py
Traceback (most recent call last):
CacheInfo(hits=0, misses=0, maxsize=100, currsize=0)
File "C:/Users/pbreach/PycharmProjects/project/main.py", line 4, in <module>
print(package.a(None).cache_info())
AttributeError: 'function' object has no attribute 'cache_info'
这意味着从b
module_b
导入时module_a
未装饰。
为什么这只发生在第二行?什么可能是实现这个目标的方法?
我在__init__.py
或main.py
导入过程中进行装饰很好,但我不想在package
中的每个模块中应用装饰器,就像我的情况那样他们中有不少人。
我认为问题是因为globals
中的__init__.py
与b
导入module_a
时的名称空间不同,这意味着同一个函数有两个不同的实例。有办法解决这个问题吗?
答案 0 :(得分:1)
您正在b
中module_b
导入module_a
,然后才有机会使用functools.lru_cache
进行装饰。
我看到唯一可行的替代方法是首先明确地修饰导入并在其他子模块中使用的函数,然后将装饰器应用于所有其他函数。
使用您的示例,首先从b
装饰module_b
,然后装饰其余部分:
from package import module_b
import types
# This is the decorator that will be used
from functools import lru_cache
module_b.b = lru_cache(maxsize=100)(module_b.b)
from .module_a import *
from .module_b import *
for name, obj in list(globals().items()):
if isinstance(obj, types.FunctionType):
globals()[name] = lru_cache(maxsize=100)(obj)
正如我在评论中所说的那样,另一个选择是在模块中使用if
子句,其中包含其他模块中包含的函数,包括将在那里进行包装。
所以在module_b.py
中,您可以执行以下操作:
if __name__ != '__main__':
from functools import lru_cache
b = lru_cache(b)
这只能捕获模块未以__main__
运行的情况。现在,当另一个模块包含此模块并且它的主体被执行时,将在此处执行包装而不是__init__.py
。