python - 有没有办法访问用装饰器定义的 Python 函数的命名空间?
问题描述
假设我使用缓存装饰器来定义一个新函数,如下所示:
def cached(funcy):
cache = dict()
def cache_funcy(x):
if x in cache:
return cache[x]
else:
print cache
result = funcy(x)
cache[x] = result
return result
return cache_funcy
@cached
def triple(x):
return 3*x
调用该函数triple
四次会产生以下输出:
>>> triple(1)
{}
3
>>> triple(2)
{1: 3}
6
>>> triple(2)
6
>>> triple(4)
{1: 3, 2: 6}
12
我的理解是该函数triple
可以访问本地调用的字典,cache
因为该字典存在于triple
定义的命名空间中。此字典在外部全局范围内不可直接访问。
cache
是否可以通过函数的某种属性访问该字典triple
?
注意:我想知道是否可以在不显式创建viacache
的属性的情况下执行此操作,例如.triple
cache_funcy.cache = cache
cached
解决方案
实际上,这个 dict 并没有存储在函数的 local-namespace 中,它是一个自由变量,所以它存储在函数闭包中。在 Python 2中,考虑:
In [1]: def cached(funcy):
...: cache = dict()
...: def cache_funcy(x):
...: if x in cache:
...: return cache[x]
...: else:
...: print cache
...: result = funcy(x)
...: cache[x] = result
...: return result
...: return cache_funcy
...:
...: @cached
...: def triple(x):
...: return 3*x
...:
In [2]: triple(1)
{}
Out[2]: 3
In [3]: triple(2)
{1: 3}
Out[3]: 6
现在:
In [5]: triple.func_closure
Out[5]:
(<cell at 0x10e4e7be8: dict object at 0x10e7ec910>,
<cell at 0x10e7b2590: function object at 0x10e81ede8>)
第一个单元格包含dict
,第二个单元格包含被修饰的函数(这也是一个自由变量)。因此,您可以使用:
In [6]: triple.func_closure[0].cell_contents
Out[6]: {1: 3, 2: 6}
In [7]: triple.func_closure[0].cell_contents[2] = 'foo'
In [8]: triple(2)
Out[8]: 'foo'
注意,Python 3中函数的属性有些不同,这里有一个直接的属性__closure__
,所以:
In [4]: triple.__closure__
Out[4]:
(<cell at 0x1026dbc78: dict object at 0x1028d1bd0>,
<cell at 0x1026dbf48: function object at 0x1028e59d8>)
实际上,在 Python 2 中,从 Python 2.6 开始,添加了这些下划线属性是为了向前兼容,所以这个属性也存在,除非你使用的是低于 Python 2.6 的版本。
因此,出于兼容性原因,您可能应该使用__closure__
推荐阅读
- amazon-web-services - 在 AWS 上出现错误“必须指定至少一个'memory'或'memoryReservation'”
- python - Post 方法没有更新数据库 Django
- ios - UICollectionViewDragDelegate 不工作
- c# - 将数字转换为其他类型的数字并在信息丢失时出现错误
- flutter - 容器中的颤振盒装饰
- python-3.x - 如何将 x.numpy() 的形状转换为矩阵 (n,m)
- html - Angular 10:如何创建动态组件/html?
- php - Symfony 5 - 多重保护认证 - 主页认证用户
- google-cloud-platform - 在 Google API 控制台中更改项目名称而不使用 CLI 工具
- mysql - MySQL - 选择排名前 5 名