将装饰器应用于 python 包中的所有函数

Applying decorator to all functions in a python package

我的问题与 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__.pymain.py 中导入期间进行装饰,但我宁愿不必像在我的 package 中那样在每个模块中应用装饰器情况下有相当多的人。

编辑:

我认为这个问题是因为 __init__.py 中的 globalsb 导入 module_a 时的命名空间不同,这意味着相同的有两个不同的实例功能。有解决办法吗?

您在 module_a 中从 module_b 导入 b,然后才有机会用 functools.lru_cache 修饰它。

我看到的唯一可行的选择是首先显式装饰在其他子模块中导入和使用的函数,然后将装饰器应用于所有其他函数。

使用您的示例,首先从 module_b 修饰 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.